package com.tbit.uqbike.client.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.common.config.TbitProperties;
import com.tbit.common.constant.*;
import com.tbit.common.entity.*;
import com.tbit.common.entity.openapi.MobileNameAuthResult;
import com.tbit.common.utils.TbitHttpServiceUtil;
import com.tbit.uqbike.center.pojo.MachineStatus;
import com.tbit.uqbike.client.business.user_op_log.service.UserOpLogService;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.constant.SystemConstant;
import com.tbit.uqbike.client.constant.enums.FaceTimeCheckEnum;
import com.tbit.uqbike.client.constant.enums.PayApiTypeEnum;
import com.tbit.uqbike.client.dao.capital.UserAdaPayLogDao;
import com.tbit.uqbike.client.dao.core.*;
import com.tbit.uqbike.client.dao.log.RefundOrderDao;
import com.tbit.uqbike.client.dao.log.RideUseRefundLogDao;
import com.tbit.uqbike.client.dao.log.UserRefundLogDao;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.dto.RefundMoneyReturnDTO;
import com.tbit.uqbike.client.pojo.newEntity.*;
import com.tbit.uqbike.client.remote.WeixinPay;
import com.tbit.uqbike.client.remote.adapay.PaymentUtil;
import com.tbit.uqbike.client.remote.nameauth.AliyunNameAuthUtil;
import com.tbit.uqbike.client.remote.nameauth.XCHttpClient;
import com.tbit.uqbike.client.remote.sms.ClzySmsAuthUtil;
import com.tbit.uqbike.client.remote.sms.PhoneAuthHttpClient;
import com.tbit.uqbike.client.remote.tenxun.GetEidToken;
import com.tbit.uqbike.client.remote.tenxun.models.DetectAuthResponse;
import com.tbit.uqbike.client.remote.zfb.ZFBReturn;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.*;
import com.tbit.uqbike.client.util.aliyun.OssFileUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

@Slf4j
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    UserToAgentDao userToAgentDao;
    @Autowired
    UserAccountMoneyDao userAccountMoneyDao;
    @Autowired
    UserAccountMoneyService userAccountMoneyService;
    @Autowired
    RechargeMerchantService rechargeMerchantService;
    @Autowired
    RideUseLogService rideUseLogService;
    @Autowired
    private UserDao userDao;
    @Autowired
    private SmsAuthConfigService smsAuthConfigService;
    @Autowired
    private NewUserActivityDao newUserActivityDao;
    @Autowired
    private GiftCardUserService giftCardUserService;
    @Autowired
    private CouponDao couponDao;
    @Autowired
    private CouponUserDao couponUserDao;
    @Autowired
    private PayPointService payPointService;
    @Autowired
    private WeixinPayConfigService weixinPayConfigService;
    @Autowired
    private UserWxzffService userWxzffService;
    @Autowired
    private ZfbPayConfigService zfbPayConfigService;
    @Autowired
    private MachineDao machineDao;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private AgentDepositVipService agentDepositVipService;
    @Autowired
    private RideCardUserService rideCardUserService;
    @Autowired
    private VipCardLogService vipCardLogService;
    @Autowired
    private RechargeDao rechargeDao;
    @Autowired
    private UserAccountLogService userAccountLogService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private UserBlackListDao userBlackListDao;
    @Autowired
    private AdaPayLogService adaPayLogService;
    @Autowired
    private UserAdaPayLogDao userAdaPayLogDao;
    @Autowired
    private MachineBorrowDao machineBorrowDao;
    @Autowired
    private AdaPayService adaPayService;
    @Autowired
    private UserAdaPayMoneyService userAdaPayMoneyService;
    @Autowired
    private UserRewardLogService userRewardLogService;
    @Autowired
    private UserRefundLogDao userRefundLogDao;
    @Autowired
    private UserFaceDataDao userFaceDataDao;
    @Autowired
    private SpecialAuthService specialAuthService;
    @Autowired
    private WeixinPayService weixinPayService;
    @Autowired
    private PushMsgLogService pushMsgLogService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private RideUseRefundLogDao rideUseRefundLogDao;
    @Autowired
    private BrandRemoteConfigDao brandRemoteConfigDao;
    @Autowired
    private AccountConfigExtDao accountConfigExtDao;
    @Autowired
    private TerControlService terControlService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private UserFaceLogService userFaceLogService;
    @Autowired
    private TbitProperties tbitProperties;
    @Autowired
    private RefundOrderDao refundOrderDao;
    @Autowired
    private RechargePayTypeDao rechargePayTypeDao;
    @Autowired
    private UserOpLogService userOpLogService;
    @Resource
    private MachineStatusService machineStatusService;
    @Resource
    private ParkPointService parkPointService;
    @Resource
    private ProhibitAreaService prohibitAreaService;
    @Autowired
    private UserBlackListService userBlackListService;

    @Setter
    @Value("${payment.chargeReturnBikeGapSeconds:60}")
    private int chargeReturnBikeGapSeconds = 60;

    /**
     * 电动车骑行最小年龄限制默认值
     */
    public static final Integer CERTIFICATION_MIN_AGE_ELECTRIC_BIKE = 16;
    public static final Integer CERTIFICATION_MAX_AGE = 65;
    /**
     * 自行车骑行最小年龄限制默认值
     */
    public static final Integer CERTIFICATION_MIN_AGE_BIKE = 12;

    /**
     * 实名认证最小年龄限制配置 key
     */
    public static final String CERTIFICATION_AGE_LIMIT_MIN_KEY = "certificationAgeLimitMin";
    /**
     * 实名认证最大年龄限制 key
     */
    public static final String CERTIFICATION_AGE_LIMIT_MAX_KEY = "certificationAgeLimitMax";
    @Autowired
    private BrandShareRelationService brandShareRelationService;


    @SuppressWarnings("all")
    public static <K, V> Map<K, V> createMap(Object... args) {
        Map<K, V> map = new HashMap<>(32);
        for (int i = 0; i < args.length; i += 2) {
            map.put((K) args[i], (V) args[i + 1]);
        }
        return map;
    }

    @Override
    @Transactional
    public void insert(User user, Integer agentId) {
        userDao.insert(user);
        if (agentId != null) {
            NewUserActivity newUserActivity = newUserActivityDao.getByAccountId(agentId);
            if (newUserActivity != null && newUserActivity.valid()) {
                /**获取用户userId*/
                User userDB = getByAccountIdAndPhone(user.getAccountId(), user.getPhone());
                /**赠送金额不为空*/
                if (newUserActivity.getGiveMoney() > 0) {
                    String dateTime = DateTimeUtil.getNowTime();
                    GiftCardUser giftCardUser = new GiftCardUser();
                    giftCardUser.setMoney(newUserActivity.getGiveMoney());
                    giftCardUser.setAccountId(agentId);
                    giftCardUser.setUserId(userDB.getUserId());
                    giftCardUser.setUpdateTime(dateTime);
                    giftCardUserService.insert(giftCardUser, "新用户赠送礼品卡", BusinessConstant.BUSINESS_RECHARGE_GIF_GIVE, null, dateTime);
					/*addUserCapitalChange(userDB,agentId,"",PayPlatformContent.PAY_GIFCARD,BusinessConstant.BUSINESS_RECHARGE_GIF_GIVE,DateTimeUtil.getNowTime(),
							"","新用户赠送礼品卡",agentId,0L,giftCardUser.getMoney().longValue());
					addBalanceChange(userDB,agentId,"",DateTimeUtil.getNowTime(),BalanceLogChangeTypeContent.capitalType_giftCard,BusinessConstant.BUSINESS_RECHARGE_GIF_GIVE,
							giftCardUser.getMoney().longValue(),giftCardUser.getMoney().longValue(),"新用户赠送礼品卡");*/
                    addUserAccountLog(userDB, DateTimeUtil.getNowTime(), newUserActivity.getGiveMoney().doubleValue(), RechargeConstant.MONEY_TYPE_GIF, RechargeConstant.RECHARGE_TYPE_RECHARGE
                            , "新用户赠送礼品卡");
                }
                /**赠送优惠券*/
                if (newUserActivity.getCouponId() != null) {
                    Coupon coupon = couponDao.getByCouponId(newUserActivity.getCouponId());
                    if (coupon != null && DateTimeUtil.getTimeDF(DateTimeUtil.getNowTime(), coupon.getEndTime()) > 0) {
                        List<CouponUser> userList = new LinkedList<>();
                        for (int i = 0; i < newUserActivity.getCouponCount(); i++) {
                            CouponUser couponUser = new CouponUser();
                            couponUser.setAccountId(agentId);
                            couponUser.setUserId(userDB.getUserId());
                            couponUser.setCouponId(newUserActivity.getCouponId());
                            couponUser.setIsUse(0);
                            couponUser.setIsValid(1);
                            couponUser.setRecTime(DateTimeUtil.getNowTime());
                            couponUser.setExpireTime(
                                    DateTimeUtil.DateTimeToString(DateTimeUtil.addDay(new Date(), newUserActivity.getValidDays())));
                            userList.add(couponUser);
                        }
                        if (userList.size() > 0) {
                            couponUserDao.addBatch(userList);
                        }
                    } else {
                        log.info("优惠券不存在或已过期,代理id=" + agentId + ",优惠券id=" + newUserActivity.getCouponId());
                    }
                }

            }
        }

    }

    /**
     * 用户资金变动日志入库（旧表）
     *
     * @param user
     * @param dateTime
     * @param money
     * @param moneyType
     * @param type
     * @param remark
     */
    public void addUserAccountLog(User user, String dateTime, Double money, Integer moneyType, Integer type, String remark) {
        UserAccountLog userAccountLog = new UserAccountLog();
        userAccountLog.setAccountId(user.getAccountId());
        userAccountLog.setUserId(user.getUserId());
        userAccountLog.setOperaTime(dateTime);
        userAccountLog.setMoney(money);
        userAccountLog.setMoneyType(moneyType);
        userAccountLog.setType(type);
        userAccountLog.setRemark(remark);
        userAccountLogService.insert(userAccountLog);
    }

    public void addBalanceChange(User user, Integer adAccountId, String orderNO, String dateTime, Integer capitalType, Integer businessType
            , Long startMoney, Long changeMoney, String remark) {
			/*BalanceChange balanceChange = new BalanceChange(user.getUserId(),user.getPhone(),user.getName(),adAccountId
					,user.getAccountId(),startMoney,changeMoney
					,startMoney+changeMoney, PayPlatformContent.PAY_RECHARGE
					,capitalType,businessType
					,DateTimeUtil.StringToDateTime(dateTime).getTime(),remark,orderNO);
			pushMsgLogService.sendPushLog(JSON.toJSONString(balanceChange), TableNameConstant.BALANCECHANGE);*/
        userAccountMoneyService.editMoney(user, orderNO, changeMoney, capitalType, adAccountId
                , PayPlatformContent.PAY_RECHARGE, DateTimeUtil.StringToDateTime(dateTime).getTime(), remark, businessType);
    }

    public void addUserCapitalChange(User user, Integer adAccountId, String orderNO, Integer capitalType, Integer businessType,
                                     String dateTime, String details, String remark, Integer originalAccountId, Long changeStartMoney, Long changeEndMoney) {
        UserCapitalChange userCapitalChange = new UserCapitalChange(user.getUserId(), user.getPhone(), user.getName(), adAccountId,
                user.getAccountId(), changeStartMoney, changeEndMoney, changeEndMoney, PayPlatformContent.PAY_RECHARGE, capitalType,
                businessType, details, null, null, null, orderNO, orderNO, DateTimeUtil.StringToDateTime(dateTime).getTime(),
                remark, originalAccountId);
        pushMsgLogService.sendPushLog(JSON.toJSONString(userCapitalChange), TableNameConstant.USERCAPITALCHANGE);
    }

    @Override
    public User getByAccountIdAndPhone(Integer accountId, String phone) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("accountId", accountId);
        params.put("phone", phone);


        return userDao.getByAccountIdAndPhone(params);
    }

    @Override
    public void update(User user) {
        userDao.update(user);


    }

    @Override
    public User getByUserId(Integer userId) {
        User user = userDao.getByUserId(userId);

        return user;
    }

    @Override
    public void updateDeposit(Integer userId, Integer deposit) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("userId", userId);
        params.put("deposit", deposit);
        params.put("updateTime", DateTimeUtil.getNowTime());


        userDao.updateDeposit(params);
    }

    @Override
    public void updateMoney(Integer userId, Double money) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("userId", userId);
        params.put("money", money);
        params.put("updateTime", DateTimeUtil.getNowTime());

        // 插入用户操作日志
        User user = getByUserId(userId);
        userOpLogService.insertSingle(UserOpLog.builder()
                .accountId(user.getAccountId())
                .opUser(user.getName())
                .opUserPhone(user.getPhone())
                .targetUser(user.getName())
                .targetUserPhone(user.getPhone())
                .operation("用户余额变动")
                .opType(money > 0 ? "充值" : "用户使用")
                .opPlatform(OperationPlatform.CLIENT.code)
                .opTime(LocalDateTime.now())
                .result(true)
                .detail("变动金额: " + money / 100d + "元")
                .build());

        userDao.updateMoney(params);
    }

    @Override
    @Transactional
    public void updateDepositAndMoney(Integer userId, Integer deposit, Double depositMoney) {
        User user = getByUserId(userId);
        if (user != null) {
            if (user.getDepositState() == 0) {
                /**修改押金状态*/
                updateDeposit(userId, deposit);
            }
            /**修改押金金额*/
            updateDepositMoney(userId, depositMoney);
        }


    }

    @Override
    public void updateDepositMoney(Integer userId, Double money) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("userId", userId);
        params.put("money", money);
        params.put("updateTime", DateTimeUtil.getNowTime());


        userDao.updateDepositMoney(params);
    }

    @Override
    public void updateNameAuth(Integer userId, String idNO, String name) {
        Map<String, Object> params = new HashMap<>();
        params.put("userId", userId);
        params.put("idNO", idNO);
        params.put("name", name);
        params.put("nameAuth", true);
        params.put("updateTime", DateTimeUtil.getNowTime());

        userDao.updateNameAuth(params);
    }

    @Override
    public void authName(Integer userId, Integer accountId, String idNO, String name, Integer operationAreaId) {
        log.info("[实名认证] 品牌{} 区域{} 用户{} 进行实名认证", accountId, operationAreaId, userId);
        authCount(userId, accountId);
        // 判断区域内是否有运营自行车
        boolean isBikeExist = machineDao.checkIfExistBike(operationAreaId) != null;
        Set<String> targetConfigKeys = new HashSet<>();
        targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MIN_KEY);
        targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MAX_KEY);
        Map<String, String> configMap = accountConfigExtDao.selectByAccountIdAndKeys(operationAreaId, targetConfigKeys).stream()
                .collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
        int minAge = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MIN_KEY))
                .map(Integer::parseInt)
                // 如果有自行车运营，则设置默认最小年龄为 12，否则 16
                .orElse(isBikeExist ? CERTIFICATION_MIN_AGE_BIKE : CERTIFICATION_MIN_AGE_ELECTRIC_BIKE);
        int maxAge = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MAX_KEY))
                .map(Integer::parseInt)
                .orElse(CERTIFICATION_MAX_AGE);

        // 是否满16周岁
        if (!AgeUtil.checkIsMan(idNO, minAge)) {
            throw new BaseException("-3025", "未满 " + minAge + " 周岁");
        }

        if (AgeUtil.checkIsMan(idNO, maxAge)) {
            throw new BaseException("-3057", "用户年龄超出" + maxAge + "周岁，为了您身体安全，限制用车");
        }

        /**身份证号码是否被加入黑名单*/
        List<UserBlackList> userBlackLists = userBlackListService.getByIdNO(idNO);
        if (!userBlackLists.isEmpty()) {
            for (UserBlackList userBlackList : userBlackLists) {
                if (userBlackList != null &&
                        DateTimeUtil.getTimeDF(DateTimeUtil.getNowTime(), userBlackList.getExpireTime() + " 00:00:00") > 0) {
                    throw new BaseException("-3036", "被加入黑名单，功能被限制");
                }
            }
        }

        /*
        校验身份证使用次数
         */
        Map<String, Object> params = new HashMap<>();
        params.put("accountId", accountId);
        params.put("idNO", idNO);
        List<User> userDB = userDao.getByIdNOAndId(params);
        Integer num = 3;
        String authTimes = cacheService.getBrandRemoteConfigValue(accountId, BrandConstant.USER_NAMEAUTH_NUM);
        if (StringUtils.isNotBlank(authTimes)) {
            num = Integer.valueOf(authTimes);
        }
        if (userDB.size() >= num) {
            throw new BaseException("-3052", "该身份证已使用");
        }

        // 是否开启运营商三要素开关
        AccountConfigExt mobileNameAuthSwitch = accountConfigExtDao.selectByAccountIdAndKey(operationAreaId, AccountConfigExtConstant.NAME_AUTH_TYPE);
        log.info("[实名认证] 品牌{} 区域{} 运营商三要素开关: {}", accountId, operationAreaId, mobileNameAuthSwitch);
        if (mobileNameAuthSwitch != null && "1".equals(mobileNameAuthSwitch.getParamValue())) {
            if (authMobile(name, idNO, userId)) {
                updateNameAuth(userId, idNO, name);
                /**兼容根据身份证设置性别生日*/
                userDao.editSex(userId);
                /**兼容用户参与活动记录用户名称*/
                userRewardLogService.editName(name, userId);
            } else {
                throw new BaseException("-3021", "实名认证失败,请检查身份证号码、姓名或者手机号是否有误");
            }
        }
        // 普通二要素实名认证
        else {
            if (authentication(name, idNO, userId)) {
                updateNameAuth(userId, idNO, name);
                /**兼容根据身份证设置性别生日*/
                userDao.editSex(userId);
                /**兼容用户参与活动记录用户名称*/
                userRewardLogService.editName(name, userId);
            } else {
                throw new BaseException("-3021", "实名认证失败,请检查身份证号码或者姓名是否有误");
            }
        }
    }

    private boolean authMobile(String name, String idNO, Integer userId) {
        User user = userDao.getByUserId(userId);
        if (user == null) {
            log.error("[运营商三要素认证] 未找到用户所属品牌");
            return false;
        }
        Integer accountId = user.getAccountId();
        // 优驱api
        JSONObject jsonObject = cacheService.getBrandJsonConfig(accountId, BrandConstant.TBIT_CONFIG);
        if (jsonObject.isEmpty()) {
            return false;
        }
        String appId = jsonObject.getString("appId");
        String nonce = jsonObject.getString("nonce");
        String appSecret = jsonObject.getString("appSecret");
        MobileNameAuthResult result = TbitHttpServiceUtil.mobileNameAuth(appId, nonce, appSecret, user.getPhone(), name, idNO);
        return result.getResult();
    }

    /**
     * 多种方式的实名认证
     *
     * @return 是否通过实名认证
     */
    private Boolean authentication(String name, String idNO, Integer userId) {
        //是否实名认证成功
        boolean flag = false;
        User user = userDao.getByUserId(userId);
        if (user != null) {
            Integer accountId = user.getAccountId();
            boolean isAuth = false;
            /**实名认证类型 0寻程、1阿里云、2创蓝、3泰比特*/
            Integer type = Optional.ofNullable(cacheService.getBrandRemoteConfig(accountId, BrandConstant.NAMEAUTH_TYPE))
                    .map(x -> Integer.valueOf(x.getParamKV().substring(x.getParamKV().indexOf("=") + 1)))
                    .orElse(-1);
            if (type == 3) {
                JSONObject jsonObject = cacheService.getBrandJsonConfig(accountId, BrandConstant.TBIT_CONFIG);
                if (!jsonObject.isEmpty()) {
                    String appId = jsonObject.getString("appId");
                    String nonce = jsonObject.getString("nonce");
                    String appSecret = jsonObject.getString("appSecret");
                    flag = TbitHttpServiceUtil.nameAuth(appId, nonce, appSecret, name, idNO);
                }
                isAuth = true;
            }
            /**3要素认证*/
            BrandRemoteConfig config = cacheService.getBrandRemoteConfig(accountId, BrandConstant.ALIYUN_NAMEAUTH_THREE);
            boolean isAuth3 = Optional.ofNullable(config)
                    .map(x -> x.getParamKV().contains("&"))
                    .orElse(false);
            if (!isAuth && isAuth3) {
                String key = config.getParamKV();
                String[] strings = key.split("&");
                String host = strings[0].substring(strings[0].indexOf("=") + 1);
                String path = strings[1].substring(strings[1].indexOf("=") + 1);
                String appcode = strings[2].substring(strings[2].indexOf("=") + 1);
                flag = AliyunNameAuthUtil.nameAuth3(host, path, appcode, idNO, user.getPhone(), name);
                isAuth = true;
            }
            /**创蓝实名认证*/
            BrandRemoteConfig brandRemoteConfigDB = cacheService.getBrandRemoteConfig(accountId, BrandConstant.CLYZ_NAMEAUTH);
            if (!isAuth && brandRemoteConfigDB != null) {
                String key = brandRemoteConfigDB.getParamKV();
                String[] strings = key.split("&");
                String appKey = strings[1].substring(strings[1].indexOf("=") + 1);
                String appId = strings[0].substring(strings[0].indexOf("=") + 1);
                flag = ClzySmsAuthUtil.nameAuthBySign(appId, appKey, name, idNO);
                isAuth = true;
            }
            /**阿里云实名认证*/
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(accountId, BrandConstant.ALIYUN_NAMEAUTH);
            if (!isAuth && brandRemoteConfig != null) {
                /**新阿里云实名认证*/
                String appcode = brandRemoteConfig.getParamKV().substring(brandRemoteConfig.getParamKV().indexOf("=") + 1);
                flag = AliyunNameAuthUtil.nameAuth(appcode, idNO, name);
                isAuth = true;
            }
            /**寻程实名*/
            SmsAuthConfig smsAuthConfig = smsAuthConfigService.getByAccountId(accountId);
            if (!isAuth && smsAuthConfig != null && smsAuthConfig.getFrom() == 0) {
                flag = XCHttpClient.authName(smsAuthConfig.getNameAuthKey(), idNO, name);
            } else if (!isAuth && smsAuthConfig != null && smsAuthConfig.getFrom() == 1) {
                flag = PhoneAuthHttpClient.authName(smsAuthConfig.getNameAuthKey(), idNO, name);
            }
        }
        return flag;
    }

    @Override
    public void editSex(Integer userId) {
        /**兼容根据身份证设置性别生日*/
        userDao.editSex(userId);
    }

    /**
     * 实名认证次数计数
     *
     * @param userId    用户id
     * @param accountId 品牌id
     */
    private void authCount(Integer userId, Integer accountId) {
        String authCountKey = RedisConstant.USER_AUTH_COUNT_PREFIX + userId;
        String authStr = redisService.get(authCountKey);
        long cacheTime = DateUtil.between(new Date(), DateUtil.beginOfDay(DateUtil.tomorrow()), DateUnit.SECOND);
        BrandRemoteConfig remoteConfig = cacheService.getBrandRemoteConfig(accountId, BrandConstant.USER_AUTH_COUNT);
        //最大实名认证次数 默认5次
        int maxCount = remoteConfig == null ? 5 : Integer.parseInt(remoteConfig.getParamKV());
        //当天该用户实名认证次数
        Integer authCount = authStr == null ? 0 : Integer.parseInt(authStr);
        if (authCount >= maxCount) {
            throw new BaseException("-99999", "今日提交次数已达上限");
        }
        authCount++;
        redisService.add(authCountKey, authCount.toString(), (int) cacheTime);
    }

    @Override
    public void insertOther(User user) {
        userDao.insertOther(user);
    }

    @Override
    public void setOpenId(String code, User user, Integer accountId) {
        /**支付配置信息(1:微信APP支付；2：微信小程序支付)*/
        WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAccountId(accountId, 2);
        if (weixinPayConfig == null) {
            throw new BaseException("-2006", "该品牌微信支付未配置");
        }
        String openId = payPointService.getOpenid(code, weixinPayConfig.getAppId(), weixinPayConfig.getSecret());
        if (openId != null) {
            UserWxzff userWxzff = new UserWxzff();
            userWxzff.setOpenId(openId);
            userWxzff.setAccountId(user.getAccountId());
            userWxzff.setUserId(user.getUserId());
            userWxzff.setAccountId(accountId);
            userWxzff.setUpdateTime(new Date());
            userWxzffService.add(userWxzff);
        }
    }

    @Override
    public String faceCheck(User user, String userCode) {
        machineCheck(userCode);
        User userDB = getUser(user.getUserId());
        /**格式为MerchantId=xxx&secretId=xxx&secretKey=xxx*/
        String[] strings = getKV(user.getAccountId(), BrandConstant.EZT_FACE);
        String token = GetEidToken.getToken(strings[1].substring(strings[1].indexOf("=") + 1)
                , strings[2].substring(strings[2].indexOf("=") + 1), strings[0].substring(strings[0].indexOf("=") + 1), userDB.getName(), userDB.getIdNO());
        return token;
    }

    /**
     * 优驱人脸识别方案
     *
     * @param faceData  本地人脸信息
     * @param base64Str 图片字符串
     * @param user      用户信息
     * @param ossConfig oss配置
     * @param face_sm   本地相似度
     * @return
     */
    public boolean youquFaceCheck(UserFaceData faceData, String base64Str, User user, OssConfig ossConfig, Integer face_sm) {
        boolean faceResult = false;
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(),
                BrandConstant.TBIT_CONFIG);
        if (!jsonObject.isEmpty()) {
            String appId = jsonObject.getString("appId");
            String nonce = jsonObject.getString("nonce");
            String appSecret = jsonObject.getString("appSecret");
            /**活体检测*/
//      result= TbitHttpServiceUtil.liveDetect(appId,nonce,appSecret,base64Str);
//      if(!result){
//        throw new BaseException("-20008", "人脸识别失败,当前人脸非活体");
//      }
            /**上传oss*/
            String imageId = UUID.randomUUID().toString();
            String imageUrl = "";
            try {
                imageUrl = OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(base64Str), ossConfig, imageId, "jpg", OssFileUtils.IMAGE);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            String url = null;
            if (faceData != null) {
                url = faceData.getUrl();
            }
            /**人脸身份证对比活体一体化检测*/
            String result = TbitHttpServiceUtil.realPerson(appId, nonce, appSecret, user.getName(), user.getIdNO(), url, imageUrl);
            if (result == null) {
                return false;
            }
            if (result.isEmpty()) {
                throw new BaseException("-20008", "人脸识别失败,请联系客服为您处理");
            }
            JSONObject object = JSON.parseObject(result);
            if (!object.getBoolean("alive")) {
                throw new BaseException("-20008", "人脸识别失败,当前人脸非活体");
            }
            if (object.getDouble("similarity") * 100 > face_sm) {
                addLocalFace(user, base64Str, ossConfig, object.getDouble("similarity"));
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_SUCCESS, "", object.getDouble("similarity"), "优驱");
                faceResult = true;
            } else {
                //相似度不够也记录
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_NOT_SUCCESS, "", object.getDouble("similarity"), "优驱");
            }
            /**人脸身份证对比*/
//      if(faceData==null){
//        result= TbitHttpServiceUtil.idfaceIdentity(appId,nonce,appSecret,imageUrl,user.getName(),user.getIdNO());
//        if(!result){
//          throw new BaseException("-20008", "人脸识别失败");
//        }
//      }else{
//        /**人脸比对*/
//        result= TbitHttpServiceUtil.faceCompare(appId,nonce,appSecret,imageUrl,faceData.getUrl(),face_sm);
//        if(!result){
//          throw new BaseException("-20008", "人脸识别失败");
//        }
//      }
        }

        return faceResult;
    }

    @Override
    public void faceCheckNew(User user, String base64Str, Machine machine) {
        User userDB = getUser(user.getUserId());
        Integer second = getSecond(machine.getAccountId());
        OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");

        //根据区域id获取对应配置
        HashMap<String, Object> map = new HashMap<>();
        map.put("accountId", machine.getAccountId());
        map.put("paramKey", "faceRecognitionConfig");
        AccountConfigExt byKey = accountConfigExtDao.getByKey(map);
        /**兼容人脸识别本地缓存*/
        AccountConfigExt accountConfigExt1 = cacheService.getAccountConfigExtByKey(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_FACE_BD);
        Integer face_bd = Optional.ofNullable(accountConfigExt1)
                .map(x -> Integer.valueOf(x.getParamValue())).orElse(80);
        //活体检测
        UserFaceData faceData = userFaceDataDao.getByUserId(user.getUserId());
        /**优驱人脸识别方案*/
        String cfType = Optional.ofNullable(byKey).map(AccountConfigExt::getParamValue).orElse("-1");
        if ("1104".equalsIgnoreCase(cfType)) {
            boolean result = youquFaceCheck(faceData, base64Str, userDB, ossConfig, face_bd);
            if (result) {
                redisService.add(RedisConstant.REDIS_USER_FACE + user.getUserId(), "1", second);
                return;
            } else {
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_FAIL, "人脸识别失败", -1d, "优驱");
                throw new BaseException("-20008", "人脸识别失败");
            }
        }

        boolean localResult = selfDevFaceService(machine.getAccountId(), ossConfig, user.getUserId(), base64Str, user.getAccountId());
        if (localResult) {
            redisService.add(RedisConstant.REDIS_USER_FACE + user.getUserId(), "1", second);
            return;
        }

        Map<String, Object> pushMap = new HashMap<>();
        pushMap.put("userId", user.getUserId());
        pushMap.put("userCode", machine.getUserCode());
        pushMap.put("machineNO", machine.getMachineNO());
        pushMap.put("addTime", DateTimeUtil.getNowTime());
        pushMap.put("accountId", machine.getAccountId());
        pushMap.put("brandId", user.getAccountId());
        /**渠道类型1华为0阿里*/

        pushMap.put("type", "1");
        /**增加人脸识别相似度校验*/

        Double aly_Similarity = getConfigValue(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_FACE_ALY);


        Map<String, String> huaweiMap = getHuaWeiRequestMap(userDB.getAccountId());
        //ParamValue 0=阿里 1001=华为 1000=腾讯 1002优驱
        if (byKey != null && "1001".equals(byKey.getParamValue())) {
            livingCheck(huaweiMap, base64Str, userDB, ossConfig);
        }

        if (byKey != null && faceData != null && faceData.getSimilarity() >= aly_Similarity && "1001".equals(byKey.getParamValue())) {
            Boolean result = huaweiCloudFaceCompare(base64Str, faceData, huaweiMap, ossConfig, userDB, machine.getAccountId(), pushMap);
            /**日志推送开关打开*/

            if (getBooleanValue(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_PUSHLOG)) {
                terControlService.send(RabbitMQData.queueExchange, RabbitMQData.slskey, String.valueOf(GatewayProtocol.MQ_MSG_ID_SLS_LOG_FACE_PUSH), "", JSON.toJSONString(pushMap));
            }
            if (!result) {
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_NOT_SUCCESS, "", (Double) pushMap.get("similarity"), "华为云");
                throw new BaseException("-20008", "人脸识别失败");
            } else {
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_SUCCESS, "", (Double) pushMap.get("similarity"), "华为云");
            }
        } else {
            SmsAuthConfig smsAuthConfig = smsAuthConfigService.getByAccountId(user.getAccountId());
            if (smsAuthConfig == null) {
                throw new BaseException("-160002", "该功能未配置");
            }
            String result = FaceUtil.face(smsAuthConfig.getFaceCheckCode(), base64Str, userDB.getName(), userDB.getIdNO(), pushMap);
            /**返回结果为 {"error_code":0,"reason":"成功","result":{"IdCardNo":"******************","Name":"***","Validate_Result":3,"Similarity":33.0}}*/
            log.info("人脸结果=" + result);
            Integer code = 0;

            double similarity = 0D;
            if (result.contains("Validate_Result")) {
                JSONObject jsonObject = JSON.parseObject(result);
                code = jsonObject.getJSONObject("result").getInteger("Validate_Result");

                similarity = jsonObject.getJSONObject("result").getDouble("Similarity");
            }

            pushMap.put("type", "0");
            pushMap.put("similarity", similarity);
            pushMap.put("isTrue", false);


            if (code == 1 && (similarity > aly_Similarity)) {
                pushMap.put("isTrue", true);
            }
            /**日志推送开关打开*/

            if (getBooleanValue(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_PUSHLOG)) {
                terControlService.send(RabbitMQData.queueExchange, RabbitMQData.slskey, String.valueOf(GatewayProtocol.MQ_MSG_ID_SLS_LOG_FACE_PUSH), "", JSON.toJSONString(pushMap));
            }

            /**Validate_Result 验证结果	-1,身份证和姓名不一致 -2,公安库中无此身份证记录 -3,公安身份证库中没有此号码的照片-4 照片参数不合格
             *  -5 照片相片体积过大 -6,请检查图片编码 -7,照片相片体积过小 1,系统分析为同一人 ，2,系统分析可能为同一人
             *  3, 系统分析为不是同人 4,没检测到人脸 5,疑似非活体 6,出现多张脸
             *  7,身份证和姓名一致，官方人脸比对失败*/

            if (code != 1 || (similarity < aly_Similarity)) {
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_NOT_SUCCESS, "", similarity, "阿里云");
                throw new BaseException("-20008", "人脸识别失败");
            }

            if (accountConfigExt1 != null || (faceData != null && faceData.getSimilarity() < aly_Similarity)) {
                addLocalFace(user, base64Str, ossConfig, similarity);
            }
            userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_SUCCESS, "", similarity, "阿里云");
        }
        redisService.add(RedisConstant.REDIS_USER_FACE + userDB.getUserId(), "1", second);
    }

    /**
     * @param accountId  区域id
     * @param configType 配置类型
     * @return
     */

    private Double getConfigValue(Integer accountId, String configType) {
        AccountConfigExt accountConfigExt1 = cacheService.getAccountConfigExtByKey(accountId, configType);
        /**没设置 阿里云人脸默认75相似度 华为云默认93*/

        if (accountConfigExt1 == null && AccountConfigExtConstant.ACCOUNTCONFIG_FACE_ALY.equals(configType)) {
            return 75D;

        } else if (accountConfigExt1 == null && AccountConfigExtConstant.ACCOUNTCONFIG_FACE_HWY.equals(configType)) {
            return 93D;
        }

        if (accountConfigExt1 == null) {
            return 0D;
        }
        return Double.parseDouble(accountConfigExt1.getParamValue());
    }

    /**
     * 获取开关类型值
     *
     * @param accountId
     * @param configType
     * @return
     */
    private boolean getBooleanValue(Integer accountId, String configType) {
        AccountConfigExt accountConfigExt1 = cacheService.getAccountConfigExtByKey(accountId, configType);
        if (accountConfigExt1 != null) {
            return "1".equals(accountConfigExt1.getParamValue());
        }
        return false;
    }

    /**
     * 活体检测
     *
     * @param huaweiMap
     * @param base64Str base64图片
     * @param user      用户
     * @return
     */

    public void livingCheck(Map<String, String> huaweiMap, String base64Str, User user, OssConfig ossConfig) {
        try {
            if (huaweiMap.isEmpty()) {
                throw new BaseException("-20024", "人脸识别配置有误");
            }

            boolean isUpload = false;
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(user.getAccountId(), BrandConstant.BRAND_LIVE_PHOTO);
            if (brandRemoteConfig != null && Integer.valueOf(brandRemoteConfig.getParamKV().substring(brandRemoteConfig.getParamKV().indexOf("=") + 1)) == 1) {
                isUpload = true;
            }

            OssConfig ossConfig1 = new OssConfig();
            BeanUtils.copyProperties(ossConfig, ossConfig1);
            ossConfig1.setBucketName(SystemConstant.FACEBUCKETNAME);
            String imageId = UUID.randomUUID().toString();
            //判断华为静态活体检测开关是否打开(faceOpen: 0:关闭	1:打开)
            if ("1".equals(huaweiMap.get("faceOpen"))) {
                String result = FaceCompareUtil.liveDetect(base64Str, huaweiMap);
                JSONObject jsonObject = JSON.parseObject(result);

                if (jsonObject.getString("error_code") != null && "FRS.0033"
                        .equals(jsonObject.getString("error_code"))) {
                    log.error("当前请求数太多，超过流控限制" + result);
                }

                if (jsonObject.getJSONObject("result") == null) {
                    log.error("华为云静态活体检测有误返回结果:" + result);
                    throw new BaseException("-20008", "人脸识别失败");
                }
                String alive = jsonObject.getJSONObject("result").getString("alive");
                log.info("华为云静态活体检测结果:{}", alive);
                if ("false".equals(alive)) {
                    log.warn("华为云静态活体检测结果失败用户:{};明细:{}", JSON.toJSONString(user), result);

                    //log.info("非活体认证成功照片="+base64Str);
                    //FaceUtil.generateImage(base64Str, "D://data//nolive"+System.currentTimeMillis()+".jpg");
                    if (isUpload && StringUtils.isNotEmpty(SystemConstant.FACEBUCKETNAME)) {

                        // 活体(uuid.活体结果(0:不通过;1、通过))
                        imageId = String.format("nolive/%s.0", UUID.randomUUID().toString());
                        OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(base64Str), ossConfig1, imageId, "jpg", OssFileUtils.IMAGE);
                    }
                    throw new BaseException("-20008", "人脸识别失败,当前人脸非活体");

                } else {
                    //log.info("活体认证成功照片="+base64Str);
                    //FaceUtil.generateImage(base64Str, "D://data//live"+System.currentTimeMillis()+".jpg");
                    if (isUpload && StringUtils.isNotEmpty(SystemConstant.FACEBUCKETNAME)) {
                        imageId = String.format("live/%s.1", UUID.randomUUID().toString());
                        OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(base64Str), ossConfig1, imageId, "jpg", OssFileUtils.IMAGE);
                    }
                }
            }
        } catch (BaseException e) {
            String[] array = e.getMessage().split("%");
            if (array[0].equals("-20008")) {
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_FAIL, array[1] != null ? array[1] : "", -1d, "华为云");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BaseException("-20008", "人脸识别失败");
        }
    }

    /**
     * 获取华为云请求map
     *
     * @param accountId 品牌id
     * @return
     */
    public Map<String, String> getHuaWeiRequestMap(Integer accountId) {
        //判断是否开启静态活体检测
        BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(accountId, BrandConstant.HUAWEI_FACE);
        //处理华为云配置
        Map<String, String> huaweiMap = new HashMap<>();
        if (brandRemoteConfig != null) {
            String key = brandRemoteConfig.getParamKV();
            String[] split = key.split("&");
            huaweiMap.put("ak", split[0].substring(split[0].indexOf("=") + 1));
            huaweiMap.put("sk", split[1].substring(split[1].indexOf("=") + 1));
            huaweiMap.put("faceOpen", split[2].substring(split[2].indexOf("=") + 1));
            huaweiMap.put("project", split[3].substring(split[3].indexOf("=") + 1));
            huaweiMap.put("projectId", split[4].substring(split[4].indexOf("=") + 1));
        }
        return huaweiMap;
    }

    @Override
    public DetectAuthResponse faceCheckTencent(User user, String userCode, String redirectUrl) {
        Machine machine = machineCheck(userCode);
        FaceCheck faceCheck = faceTimesCheck(user.getUserId());
        User userDB = getUser(user.getUserId());
        String[] strings = getKV(user.getAccountId(), BrandConstant.TENCENT_FACE);
        /**格式为secretId=xxx&secretKey=xxx&ruleId=xxx&ruleId2=xxx*/
        OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");
        String oldBase = base64(user.getUserId(), ossConfig);
        String ruleId = strings[2].substring(strings[2].indexOf("=") + 1);
        if (StringUtils.isNotEmpty(oldBase)) {
            ruleId = strings[3].substring(strings[3].indexOf("=") + 1);
        }
        DetectAuthResponse detectAuthResponse = GetEidToken.getBizToken(strings[0].substring(strings[0].indexOf("=") + 1), strings[1].substring(strings[1].indexOf("=") + 1)
                , ruleId, userDB.getName(), userDB.getIdNO(), redirectUrl, oldBase);
        return detectAuthResponse;
    }

    /**
     * 优驱腾讯云人脸识别
     *
     * @param user        user
     * @param userCode    车辆编号
     * @param redirectUrl 回调地址
     * @return DetectAuthResponse
     */
    @Override
    public DetectAuthResponse youquFaceCheckTencent(User user, String userCode, String redirectUrl, Integer accountId) {
        Machine machine = machineCheck(userCode);
        FaceCheck faceCheck = faceTimesCheck(user.getUserId());
        DetectAuthResponse detectAuthResponse = new DetectAuthResponse();

        // A区域车跑到B区域了
        if (!machine.getAccountId().equals(accountId)) {
            throw new BaseException("车辆异常，请换一辆车骑行");
        }
        //获取优驱api配置并且判断人脸识别是否是优驱api
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(), BrandConstant.TBIT_CONFIG);

        if (!jsonObject.isEmpty()) {
            AccountConfigExt accountConfigExt = cacheService.selectByAccountIdAndKey(machine.getAccountId(), AccountConfigExtConstant.FACE_RECOGNITION_CONFIG);
            if ("1105".equals(accountConfigExt.getParamValue())) {
                String appId = jsonObject.getString("appId");
                String nonce = jsonObject.getString("nonce");
                String appSecret = jsonObject.getString("appSecret");
                User userDB = getUser(user.getUserId());
                UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
                Map<String, Object> bizToken = TbitHttpServiceUtil.getToken(appId, nonce, appSecret, redirectUrl, Objects.isNull(userFaceData) ? null : userFaceData.getUrl(), userDB.getName(), userDB.getIdNO());
                if (Objects.isNull(bizToken.get("bizToken")) || Objects.isNull(bizToken.get("url")) || Objects.isNull(bizToken.get("requestId"))) {
                    log.error("优驱腾讯云人脸识别返回参数为空:{}", JSON.toJSONString(bizToken));
                    if (bizToken.isEmpty()) {
                        throw new BaseException("人脸识别失败,请联系客服为您处理");
                    }
                    throw new BaseException("人脸识别失败");
                }
                detectAuthResponse.setBizToken(bizToken.get("bizToken").toString());
                detectAuthResponse.setUrl(bizToken.get("url").toString());
                detectAuthResponse.setRequestId(bizToken.get("requestId").toString());
                return detectAuthResponse;
            }
        }
        return detectAuthResponse;
    }

    /**
     * 获取优驱腾讯云人脸识别结果
     *
     * @param user      user
     * @param bizToken  bizToken
     * @param machine   设备信息
     * @param accountId 区域id
     * @return boolean
     */
    @Override
    public boolean getuqResult(User user, String bizToken, Machine machine, Integer accountId) {
        //获取优驱api配置并且判断人脸识别是否是优驱api
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(),
                BrandConstant.TBIT_CONFIG);
        if (!jsonObject.isEmpty()) {
            AccountConfigExt accountConfigExt = cacheService.selectByAccountIdAndKey(accountId, AccountConfigExtConstant.FACE_RECOGNITION_CONFIG);
            if ("1105".equals(accountConfigExt.getParamValue())) {
                //获取oss人脸识别图片
                OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");
                Integer second = getSecond(machine.getAccountId());

                String appId = jsonObject.getString("appId");
                String nonce = jsonObject.getString("nonce");
                String appSecret = jsonObject.getString("appSecret");

                UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
                //调用优驱api腾讯云人脸识别结果
                Map<String, Object> result = TbitHttpServiceUtil.getResult(appId, nonce, appSecret, bizToken, Objects.isNull(userFaceData) ? null : userFaceData.getUrl());
                String bestFrame = result.get("bestFrame") == null ? null : result.get("bestFrame").toString();
                if (CollectionUtil.isNotEmpty(result)) {
                    if (Objects.nonNull(result.get("result")) && "true".equals(result.get("result"))) {
                        redisService.add(RedisConstant.REDIS_USER_FACE + user.getUserId(), "1", second);
                        //识别结果为成功才存储本地缓存
                        try {
                            if (Objects.nonNull(bestFrame)) {
                                Object sim = result.get("sim");
                                addLocalFace(user,  bestFrame, ossConfig, Objects.nonNull(sim) ? Double.parseDouble(sim.toString()) : 0D);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        double sim = Double.parseDouble(result.getOrDefault("sim", "0").toString());
                        userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame, SysConstant.USER_FACE_SUCCESS, bizToken, sim / 100, "优驱腾讯");
                        return true;
                    }
                    double sim = Double.parseDouble(result.getOrDefault("sim", "0").toString());
                    userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame, SysConstant.USER_FACE_NOT_SUCCESS, bizToken, sim / 100, "优驱腾讯");
                }
            }
        }
        return false;
    }

    @Override
    public Boolean getTencentResult(User user, String bizToken, Machine machine) {
        User userDB = getUser(user.getUserId());
        Integer second = getSecond(machine.getAccountId());
        String[] strings = getKV(user.getAccountId(), BrandConstant.TENCENT_FACE);
        OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");
        String oldBase = base64(user.getUserId(), ossConfig);
        String ruleId = strings[2].substring(strings[2].indexOf("=") + 1);
        if (StringUtils.isNotEmpty(oldBase)) {
            ruleId = strings[3].substring(strings[3].indexOf("=") + 1);
        }
        String result = GetEidToken.getDetectInfo(strings[0].substring(strings[0].indexOf("=") + 1), strings[1].substring(strings[1].indexOf("=") + 1)
                , ruleId, bizToken);
        JSONObject jsonObject = JSON.parseObject(result);
        log.info("腾讯云人脸识别" + result);
        if (jsonObject == null) {
            return false;
        }
        JSONObject text = jsonObject.getJSONObject("Text");
        JSONObject bestFrame = jsonObject.getJSONObject("BestFrame");
        if (text != null && "0".equals(text.getString("ErrCode")) && "0".equals(text.getString("LiveStatus"))) {
            if (bestFrame != null && bestFrame.getString("BestFrame") != null) {
                String base64String = bestFrame.getString("BestFrame");
                UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
                if (userFaceData == null) {
                    /***/
                    addLocalFace(user, base64String, ossConfig, 0D);
                }
            }
            userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame == null ? null : bestFrame.getString("BestFrame"),
                    "0".equals(text.getString("ErrCode")) && "0".equals(text.getString("LiveStatus")) ? SysConstant.USER_FACE_SUCCESS : SysConstant.USER_FACE_NOT_SUCCESS,
                    "", text.getDoubleValue("Sim") / 100, "腾讯云");
            redisService.add(RedisConstant.REDIS_USER_FACE + userDB.getUserId(), "1", second);
            return true;
        }
        userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame == null ? null : bestFrame.getString("BestFrame"),
                SysConstant.USER_FACE_FAIL, bizToken, 0d, "腾讯云");
        return false;
    }


    @Override
    public Boolean getTencentResultLoacl(User user, String bizToken, Machine machine) {
        User userDB = getUser(user.getUserId());
        Integer second = getSecond(machine.getAccountId());
        OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");
        //获取本地人脸相似度
        AccountConfigExt accountConfigExt = cacheService.selectByAccountIdAndKey(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_FACE_BD_LOCAL);
        String faceSm = "80";
        if (accountConfigExt != null) {
            faceSm = accountConfigExt.getParamValue();
        }
        //本地认证相似度
        int face = Integer.valueOf(faceSm);
        //获取优驱api配置并且判断人脸识别是否是优驱api
        JSONObject jsonObjectBrand = cacheService.getBrandJsonConfig(user.getAccountId(), BrandConstant.TBIT_CONFIG);
        if (jsonObjectBrand != null) {
            String appId = jsonObjectBrand.getString("appId");
            String nonce = jsonObjectBrand.getString("nonce");
            String appSecret = jsonObjectBrand.getString("appSecret");
            Map<String, Object> tencentResultLoacl = TbitHttpServiceUtil.getTencentResultLoacl(tbitProperties.getUrl(), appId, nonce, appSecret, bizToken);
            if ("true".equals(tencentResultLoacl.get("result"))) {
                double sim = tencentResultLoacl.get("sim") == null ? 0D : Double.parseDouble(tencentResultLoacl.get("sim").toString());
                int simFace = (int) (sim * 100);
                if (redisService.setNx("user_face_log:insert_lock:" + bizToken, "1", 300)) {
                    userFaceLogService.insertUserFaceLogAndUpdate(user, (String) tencentResultLoacl.get("bestFrame"), simFace >= face ? SysConstant.USER_FACE_SUCCESS : SysConstant.USER_FACE_NOT_SUCCESS, "", sim, "优驱自研");
                }
                if (simFace >= face) {
                    String base64String = (String) tencentResultLoacl.get("bestFrame");
                    UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
                    if (userFaceData == null) {
                        /***/
                        addLocalFace(user, base64String, ossConfig, sim);
                    }
                    redisService.add(RedisConstant.REDIS_USER_FACE + userDB.getUserId(), "1", second);
                    return true;
                }
            } else if ("false".equals(tencentResultLoacl.get("result"))) {
                if (redisService.setNx("user_face_log:insert_lock:" + bizToken, "1", 300)) {
                    userFaceLogService.insertUserFaceLogAndUpdate(user, (String) tencentResultLoacl.get("bestFrame"),
                            SysConstant.USER_FACE_FAIL, (String) tencentResultLoacl.get("reason"), -1d, "优驱自研");
                }
                throw new BaseException((String) tencentResultLoacl.get("reason"));
            }
        }
        return false;
    }


    /**
     * 获取值
     *
     * @param brandId 品牌id
     * @param type    类型
     * @return
     */
    public String[] getKV(Integer brandId, Integer type) {
        BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(brandId,
                type);
        if (brandRemoteConfig == null) {
            throw new BaseException("-160002", "该功能未配置");
        }
        String tmp = brandRemoteConfig.getParamKV();
        String[] strings = tmp.split("&");
        return strings;
    }

    /**
     * 获取有效时间
     *
     * @param accountId
     * @return
     */
    public Integer getSecond(Integer accountId) {
        AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(accountId,
                AccountConfigExtConstant.ACCOUNTCONFIG_FACE);
        Integer second = 24 * 60 * 60;
        if (accountConfigExt != null) {
            second = Integer.valueOf(accountConfigExt.getParamValue());
        }
        return second;
    }

    /**
     * 校验
     *
     * @param userCode
     */
    public Machine machineCheck(String userCode) {
        Machine machine = machineDao.getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "设备不存在");
        }
        AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
        if (accountConfig == null || accountConfig.getFaceCheck() != 1) {
            throw new BaseException("-20010", "人脸认证未开启");
        }
        return machine;
    }

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    public User getUser(Integer userId) {
        User userDB = getByUserId(userId);
        if (userDB == null || userDB.getName() == null || userDB.getIdNO() == null) {
            throw new BaseException("-20009", "请先实名认证再人脸认证");
        }
        return userDB;
    }

    /**
     * 添加人脸本地缓存
     *
     * @param user
     * @param base64Str
     * @param ossConfig
     */
    public void addLocalFace(User user, String base64Str, OssConfig ossConfig, Double similarity) {
        /**添加图片缓存*/
        UserFaceData userFaceData = new UserFaceData();

        String path = SystemConstant.PHOTO_PATH + "/" + user.getUserId() + "A.jpg";
        /**存放本地*/
        FaceUtil.generateImage(base64Str, path);
        try {
            // 添加图片
            String imageId = UUID.randomUUID().toString();
            String url = OssFileUtils.uploadByAliYun(new FileInputStream(path), ossConfig, imageId, "jpg", OssFileUtils.IMAGE);
            userFaceData.setAccountId(user.getAccountId());
            userFaceData.setAddTime(new Date());
            /**去除前缀*/

            userFaceData.setUrl(url);
            userFaceData.setUserId(user.getUserId());
            userFaceData.setSimilarity(similarity);
            userFaceData.setRemark("");
            UserFaceData userFaceDataDB = userFaceDataDao.getByUserId(user.getUserId());
            if (userFaceDataDB == null) {
                userFaceDataDao.insert(userFaceData);
            } else {
                userFaceDataDao.edit(userFaceData);
            }
            FileUtils.deleteFile(path);
        } catch (Exception e) {
            log.error("图片上传异常", e);
        }
    }

    /**
     * 获取图片base64
     *
     * @param userId
     * @param ossConfig
     * @return
     */
    public String base64(Integer userId, OssConfig ossConfig) {
        UserFaceData userFaceData = userFaceDataDao.getByUserId(userId);
        if (userFaceData != null) {

            String path = SystemConstant.PHOTO_PATH + "/" + userId + "C.jpg";
            OssFileUtils.upload(ossConfig, userFaceData.getUrl(), path);
            String oldFace = "";
            try {
                oldFace = FaceUtil.imageToBase64(path);
                return oldFace;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return null;
    }

    /**
     * 人脸识别本地对比
     *
     * @param accountId 运营区域id
     * @param ossConfig 配置
     * @param userId    用户id
     * @param base64Str 图片base64字符串
     * @return .
     */

    public boolean selfDevFaceService(Integer accountId, OssConfig ossConfig, Integer userId, String base64Str, Integer brandId) {
        //第一次没有本地图片不做对比
        UserFaceData userFaceData = userFaceDataDao.getByUserId(userId);

        if (Objects.isNull(userFaceData)) {
            return false;
        }
        //分流配置
        Map<String, Object> configMap = Optional.ofNullable(cacheService.selectByAccountIdAndKey(accountId, AccountConfigExtConstant.ACCOUNT_CONFIG_FACE_LOCAL))
                .map(AccountConfigExt::getParamValue)
                .filter(StringUtils::isNotBlank)
                .map(this::splitConfigToMap)
                .orElse(Collections.emptyMap());
        //查看流量比例,请求是否被过滤
        boolean filtered = serviceFiltering(configMap.get("PASS_RATE"));
        if (filtered) {
            return false;
        }
        OssConfig logBucketOss = new OssConfig();
        BeanUtils.copyProperties(ossConfig, logBucketOss);
        logBucketOss.setBucketName(SystemConstant.FACEBUCKETNAME);
        //是否上传至阿里云服务
        boolean isUpload = Optional.ofNullable(cacheService.getBrandRemoteConfig(brandId, BrandConstant.BRAND_LIVE_PHOTO))
                .map(x -> Integer.parseInt(x.getParamKV().substring(x.getParamKV().indexOf("=") + 1)) == 1)
                .filter(x -> x && StringUtils.isNotEmpty(SystemConstant.FACEBUCKETNAME))
                .orElse(false);
        if (Objects.equals("1", configMap.get("FACE_LIVE"))) {
            //活体检测
            String liveKey = UUID.randomUUID().toString();
            Map<String, Object> reqMap = createMap("uuid", liveKey, "file1", base64Str);
            String reqJsonData = JSON.toJSONString(reqMap);
            try {

                boolean saveToLog = Boolean.parseBoolean(configMap.getOrDefault("log", "false").toString());
                boolean split = Boolean.parseBoolean(configMap.getOrDefault("split", "false").toString());
                redisService.lPushWithStr("live_queue", reqJsonData);
                boolean match = Optional.ofNullable(getCheckResult(liveKey, "活体")).filter(StringUtils::isNotBlank)
                        .map(JSON::parseObject)
                        .map(x -> x.getBoolean("live"))
                        .orElse(false);
                if (isUpload) {
                    if (saveToLog && !match) {
                        redisService.lPushWithStr("live_fail_log", reqJsonData);
                    }
                    String imageId = String.format(match ? "local-live/%s.1" : "local-nolive/%s.0", liveKey);
                    String fileNoPrefix = split ? FaceUtil.removeImagePrefix(base64Str) : base64Str;
                    OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(fileNoPrefix), logBucketOss, imageId, "jpg", 999);
                }
                if (!match) {
                    throw new BaseException("-20008", "人脸识别失败");
                }
            } catch (Exception e) {

                log.error("本地人脸活体检测异常", e);
                throw new BaseException("-20008", "人脸识别失败");
            } finally {
                redisService.lRemWithStr("live_queue", reqJsonData);
            }

        }
        String uploadFilePath = String.format("%s/%d.jpg", SystemConstant.PHOTO_PATH, userId);
        OssFileUtils.upload(ossConfig, userFaceData.getUrl(), uploadFilePath);
        String oldFace = "";
        try {

            oldFace = FaceUtil.imageToBase64(uploadFilePath);
        } catch (Exception e) {
            log.error("人脸加载失败", e);
        }

        String uKey = UUID.randomUUID().toString();
        Map<String, String> valMap = createMap("uuid", uKey,
                "file1", FaceUtil.concatImagePrefix(oldFace),
                "file2", FaceUtil.concatImagePrefix(base64Str));
        String faceReqData = JSON.toJSONString(valMap);
        try {
            //请求
            redisService.lPushWithStr("face_queue", faceReqData);
            JSONObject jsonObj = Optional.ofNullable(getCheckResult(uKey, "人脸")).filter(StringUtils::isNotBlank)
                    .map(JSON::parseObject)
                    .orElse(new JSONObject());
            double similarity = Optional.of(jsonObj.getDoubleValue("distance")).filter(x -> x >= 0).map(x -> (1 - x) * 100).orElse(0.0);
            if (isUpload) {
                // 上报和新照片
                // 人脸比对（uuid（表示某一次对比，流水号的意思，每次对比的2个照片这个一样）.类型（0:基准照片；1:对比照片）.用户id.匹配度)
                String imageIdOr = String.format("local-face/%s.0.%d.%d", uKey, userId, userFaceData.getSimilarity().intValue());
                String imageIdNew = String.format("local-face/%s.1.%d.%d", uKey, userId, (int) similarity);
                OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(oldFace), logBucketOss, imageIdOr, "jpg", 999);
                String fileNoPrefix = FaceUtil.removeImagePrefix(base64Str);
                OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(fileNoPrefix), logBucketOss, imageIdNew, "jpg", 999);
            }

            Boolean match = Optional.ofNullable(jsonObj.getBoolean("match")).orElse(false);
            if (match) {
                User user = new User();
                user.setUserId(userId);
                user.setAccountId(accountId);
                userFaceLogService.insertUserFaceLogAndUpdate(user, base64Str, SysConstant.USER_FACE_SUCCESS, "", similarity, "本地比对");
                return true;
            } else {
                //相似度不够也记录
                userFaceLogService.insertUserFaceLogAndUpdate(null, base64Str, SysConstant.USER_FACE_NOT_SUCCESS, "人脸识别失败", similarity, "本地比对");
            }
            throw new BaseException("-20008", "人脸识别失败:" + JSON.toJSONString(jsonObj));
        } catch (Exception e) {
            log.error("本地人脸检测异常:" + e.getMessage());
            throw new BaseException("-20008", "人脸识别失败");
        } finally {
            FileUtils.deleteFile(uploadFilePath);
            redisService.lRemWithStr("face_queue", faceReqData);
        }
    }

    private boolean serviceFiltering(Object passRate) {
        if (Objects.isNull(passRate)) {
            return true;

        }
        int randomVal = ThreadLocalRandom.current().nextInt(1, 101);
        int passVal = Integer.parseInt(passRate.toString());
        return passVal < randomVal;
    }

    private Map<String, Object> splitConfigToMap(String x) {
        if (StringUtils.isBlank(x)) {
            return null;
        }

        Map<String, Object> pMap = new HashMap<>(8);
        String[] keyArr = x.split(";");
        for (String s : keyArr) {
            String[] split = s.split("=");
            pMap.put(split[0], split[1]);
        }
        return pMap;
    }

    private String getCheckResult(String uKey, String type) throws Exception {
        String resultJson = null;
        int queryCount = 10;
        while (queryCount != 0) {
            Thread.sleep(180);
            queryCount--;
            resultJson = redisService.lPopWithStr(uKey);
            if (StringUtils.isNotBlank(resultJson)) {
                break;
            }
        }
        log.info("本地自研" + type + "请求结果" + uKey + ":" + resultJson);
        return resultJson;
    }

    @Override
    public void expireSoon(User user, Map<String, Object> map, Integer adAccountId) {
        /**过期时间限制，暂定3天*/
        long time = 3 * 24 * 3600 * 1000;
        /**免押卡快过期校验*/
        AgentDepositVip vip = agentDepositVipService.getByUserId(user.getUserId(), adAccountId);

        /**到期时间小于一天*/
        if (vip != null && DateTimeUtil.StringToDateTime(vip.getExpireDt() + " 23:59:59").getTime() - System.currentTimeMillis() <= time &&
                DateTimeUtil.StringToDateTime(vip.getExpireDt() + " 23:59:59").getTime() > System.currentTimeMillis()) {
            map.put("member", vip);
        }
        /**骑行卡快过期校验*/
        List<RideCardUser> rideCardUsers = rideCardUserService.getByUserId(user.getUserId(), adAccountId);
        rideCardUsers.forEach(rideCardUser -> {
            /**到期时间小于一天*/
            if (rideCardUser != null &&
                    DateTimeUtil.StringToDateTime(rideCardUser.getEndDate() + " 23:59:59").getTime() - System.currentTimeMillis() <= time &&
                    DateTimeUtil.StringToDateTime(rideCardUser.getEndDate() + " 23:59:59").getTime() > System.currentTimeMillis()) {
                map.put("rideCard", rideCardUser);
            }
        });
        /**vip会员卡快过期校验*/
        //VipCardLog vipCardLogs = vipCardLogService.getById(adAccountId,user.getUserId());
        List<VipCardLog> vipCardLogs = vipCardLogService.getByTime(adAccountId, user.getUserId());
        for (VipCardLog vipCardLog : vipCardLogs) {
            if (vipCardLogs != null &&
                    DateTimeUtil.StringToDateTime(vipCardLog.getExpireDate() + " 23:59:59").getTime() - System.currentTimeMillis() <= time &&
                    DateTimeUtil.StringToDateTime(vipCardLog.getExpireDate() + " 23:59:59").getTime() > System.currentTimeMillis()) {
                map.put("vipCard", vipCardLogs);
            }
        }

    }

    @Override
    public RefundMoneyReturnDTO returnMoneyNew(Integer adAccountId, double money, Integer userId, boolean isApi, Integer operatorId, String operatorName, String operatorPhone, String refundBatch) {
        validRefund(userId);
        lockUserRefund(userId);
        if (!redisService.setNx(RedisConstant.REFUND_LOCK + userId, RedisConstant.REFUND_LOCK, 5 * 60) || refundOrderDao.countPendingOrder(userId) > 0) {
            throw new BaseException("-3070", "正在退款中,请稍后再试");
        }
        try {
            /**运营区域开关判断，只有打开开关的才可以退款*/
            Integer button = 0;
            List<AccountConfigExt> accountConfigExts = cacheService.getAccountConfigExt(adAccountId);
            if (accountConfigExts.size() > 0) {
                for (AccountConfigExt a : accountConfigExts) {
                    if (AccountConfigExtConstant.ACCOUNTCONFIG_SHOWREFUND.equalsIgnoreCase(a.getParamKey())) {
                        button = Integer.valueOf(a.getParamValue());
                    }
                }
            }
            User user = getByUserId(userId);
            if (user == null) {
                throw new BaseException("-20005", "用户不存在");
            }
            /**兼容区域外提现*/
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(user.getAccountId(),
                    BrandConstant.WITHDRAW_OUT_AREA);
            if (brandRemoteConfig != null) {
                Integer buttonDB = Integer.valueOf(brandRemoteConfig.getParamKV().substring(brandRemoteConfig.getParamKV().indexOf("=") + 1));
                if (buttonDB == 1) {
                    button = buttonDB;
                }
            }
            /**用户已退款余额*/
            Double alreadyRefundMoney = 0D;
            RefundMoneyReturnDTO refundMoneyReturnDTO = new RefundMoneyReturnDTO();
            String businessNO = OrderNoUtil.getRefundNO();
            /**开关打开或者是开放api*/
            if (button == 1 || isApi) {
                MachineBorrow machineBorrow = machineBorrowDao.getByUserId(userId);
                if (machineBorrow != null) {
                    throw new BaseException("-3018", "金额退还失败，存在未结束骑行");
                }
                if (user.getMoney().intValue() <= 0) {
                    throw new BaseException("-20014", "无可用余额");
                }
                if (user.getMoney().intValue() < money) {
                    throw new BaseException("-20014", "无可用余额");
                }
                List<Recharge> recharges = rechargeDao.getByUserId(userId);
                if (recharges.size() == 0) {
                    throw new BaseException("-20013", "不存在充值记录");
                }

                recharges = getCanRefundOrder(userId, recharges);
                log.info("用户={}的可退订单列表={}", userId, JSON.toJSONString(recharges));
                /**用户余额*/
                //double money = user.getMoney();
                double totalMoney = money;
                /**退款时限限制，微信12个月，支付宝3个月*/
                for (Recharge recharge : recharges) {
                    if (!checkIsOk(recharge)) {
                        throw new BaseException("-20016", "存在超出时限订单，请联系客服人工处理");
                    }
                    money = money - recharge.getMoney();
                    if (money <= 0) {
                        break;
                    }
                }
                List<String> merchantNOList = new ArrayList<>();
                Map<Integer, List<Recharge>> adAccountIdMap = recharges.stream().collect(Collectors.groupingBy(
                        recharge -> recharge.getAdAccountId()
                ));

                Map<Integer, List<Recharge>> newmap = new HashMap<>();

                for (Map.Entry<Integer, List<Recharge>> entry : adAccountIdMap.entrySet()) {
                    List<Recharge> rechargeList = entry.getValue().stream().sorted(Comparator.comparing(Recharge::getPayTime).reversed())
                            .collect(Collectors.toList());
                    newmap.put(entry.getKey(), rechargeList);
                }
                log.info("区域id-充值订单map={}", JSON.toJSONString(newmap));
                String refundNO = null;
                //最后一个退款成功的订单号
                String lastSuccessRefundNO = null;
                for (Map.Entry<Integer, List<Recharge>> entry : newmap.entrySet()) {
                    // 区域退款金额，用来作为日志2执行的依据
                    Double areaAlreadyRefundMoney = 0d;
                    Integer areaId = entry.getKey();
                    Account account = accountService.getByAccountId(areaId);
                    List<Recharge> rechargeList = entry.getValue();
                    for (Recharge recharge : rechargeList) {
                        if (totalMoney == 0) {
                            break;
                        }
                        Double refundMoney = 0D;
                        if (totalMoney <= recharge.getCanRefundMoney()) {
                            refundMoney = totalMoney;
                            totalMoney = 0D;
                        } else {
                            refundMoney = recharge.getCanRefundMoney();
                            totalMoney = totalMoney - recharge.getCanRefundMoney();
                        }
                        /**生成退款订单号*/
                        refundNO = OrderNoUtil.getRefundNO();
                        log.info(String.format("用户退款%s,订单编号%s,退款金额%s，退款订单号%s", userId, recharge.getOrderNO(), refundMoney, refundNO));
                        double oldRefundMoney = refundMoney;
                        refundMoney = getReturnResultNew(account, recharge, refundNO, refundMoney, user, businessNO, operatorId, operatorName, operatorPhone, null, null, 0, false, refundBatch);

                        if (refundMoney > 0) {
                            lastSuccessRefundNO = refundNO;
                            merchantNOList.add(recharge.getOrderNO());
                            User beforeUser = getByUserId(userId);
                            Long beforeUserMoney = beforeUser.getMoney().longValue();
                            alreadyRefundMoney = alreadyRefundMoney + refundMoney;
                            areaAlreadyRefundMoney += refundMoney;

                            RefundOrder refundOrder = refundOrderDao.getByRefundNO(refundNO);
                            if (Objects.isNull(refundOrder)) {
                                log.error("refundorder为空，请排查refundorder插入问题,refundNO={}", refundNO);
                                refundMoneyReturnDTO.setRefundMoney(alreadyRefundMoney);
                                refundMoneyReturnDTO.setRefundBusinessNO(businessNO);
                                return refundMoneyReturnDTO;
                            }
                            String logTag = Objects.isNull(refundOrder.getLogTag()) ? (RefundConstant.LOG1 + ",") : (refundOrder.getLogTag() + RefundConstant.LOG1 + ",");
                            refundOrder.setLogTag(logTag);
                            refundOrder.setUserId(userId);
                            refundOrder.setRefundMoney(refundMoney);
                            refundOrder.setAdAccountId(areaId);
                            refundOrderDao.update(refundOrder);
                        } else {
                            totalMoney = totalMoney + (oldRefundMoney - refundMoney);
                        }
                    }
                    if (alreadyRefundMoney > 0) {
                        RefundOrder refundOrder = refundOrderDao.getByRefundNO(lastSuccessRefundNO);
                        if (Objects.isNull(refundOrder)) {
                            log.error("refundorder为空，请排查refundorder插入问题,refundNO={}", lastSuccessRefundNO);
                            refundMoneyReturnDTO.setRefundMoney(alreadyRefundMoney);
                            refundMoneyReturnDTO.setRefundBusinessNO(businessNO);
                            return refundMoneyReturnDTO;
                        }
                        refundOrder.setAreaAlreadyRefundMoney(alreadyRefundMoney);
                        String logTag = Objects.isNull(refundOrder.getLogTag()) ? (RefundConstant.LOG2 + ",") : (refundOrder.getLogTag() + RefundConstant.LOG2 + ",");
                        refundOrder.setLogTag(logTag);
                        refundOrder.setUserId(userId);
                        refundOrder.setAdAccountId(areaId);
                        // 用户区域退余额总额
                        refundOrder.setAreaAlreadyRefundMoney(areaAlreadyRefundMoney);
                        refundOrder.setBusinessNO(businessNO);
                        String merchantNOListStr = JSON.toJSONString(merchantNOList);
                        refundOrder.setMerchantNOListStr(merchantNOListStr);
                        refundOrder.setOperatorId(operatorId);
                        refundOrder.setOperatorName(operatorName);
                        refundOrder.setOperatorPhone(operatorPhone);
                        refundOrderDao.update(refundOrder);
                    }
                }

                /**成功退款金额小于总的退款金额，返回提示*/
                if (alreadyRefundMoney < money) {
                    throw new BaseException("-20022", "部分余额退还失败，请联系客服人工处理");
                }
            } else {
                throw new BaseException("-20017", "该区域未开启自动退款，请联系客服人工处理");
            }
            refundMoneyReturnDTO.setRefundMoney(alreadyRefundMoney);
            refundMoneyReturnDTO.setRefundBusinessNO(businessNO);
            return refundMoneyReturnDTO;
        } finally {
//                redisService.del(RedisConstant.REFUND_LOCK + userId);
        }
    }

    @Override
    public Double returnMoney(Integer adAccountId, double money, Integer userId, boolean isApi, Integer operatorId,
                              String operatorName, String operatorPhone) {
        validRefund(userId);
        lockUserRefund(userId);
        if (!redisService.setNx(RedisConstant.REFUND_LOCK + userId, RedisConstant.REFUND_LOCK, 5 * 60) || refundOrderDao.countPendingOrder(userId) > 0) {
            throw new BaseException("-3070", "正在退款中,请稍后再试");
        }
        try {
            /**运营区域开关判断，只有打开开关的才可以退款*/
            Integer button = 0;
            List<AccountConfigExt> accountConfigExts = cacheService.getAccountConfigExt(adAccountId);
            if (accountConfigExts.size() > 0) {
                for (AccountConfigExt a : accountConfigExts) {
                    if (AccountConfigExtConstant.ACCOUNTCONFIG_SHOWREFUND.equalsIgnoreCase(a.getParamKey())) {
                        button = Integer.valueOf(a.getParamValue());
                    }
                }
            }
            User user = getByUserId(userId);
            if (user == null) {
                throw new BaseException("-20005", "用户不存在");
            }
            /**兼容区域外提现*/
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(user.getAccountId(),
                    BrandConstant.WITHDRAW_OUT_AREA);
            if (brandRemoteConfig != null) {
                Integer buttonDB = Integer.valueOf(brandRemoteConfig.getParamKV().substring(brandRemoteConfig.getParamKV().indexOf("=") + 1));
                if (buttonDB == 1) {
                    button = buttonDB;
                }
            }
            /**用户已退款余额*/
            Double alreadyRefundMoney = 0D;
            /**开关打开或者是开放api*/
            if (button == 1 || isApi) {
                MachineBorrow machineBorrow = machineBorrowDao.getByUserId(userId);
                if (machineBorrow != null) {
                    throw new BaseException("-3018", "金额退还失败，存在未结束骑行");
                }
                if (user.getMoney().intValue() <= 0) {
                    throw new BaseException("-20014", "无可用余额");
                }
                if (user.getMoney().intValue() < money) {
                    throw new BaseException("-20014", "无可用余额");
                }
                List<Recharge> recharges = rechargeDao.getByUserId(userId);
                if (recharges.size() == 0) {
                    throw new BaseException("-20013", "不存在充值记录");
                }

                recharges = getCanRefundOrder(userId, recharges);
                /**用户余额*/
                //double money = user.getMoney();
                double totalMoney = money;
                /**退款时限限制，微信12个月，支付宝3个月*/
                for (Recharge recharge : recharges) {
                    if (!checkIsOk(recharge)) {
                        throw new BaseException("-20016", "存在超出时限订单，请联系客服人工处理");
                    }
                    money = money - recharge.getMoney();
                    if (money <= 0) {
                        break;
                    }
                }

                String businessNO = OrderNoUtil.getRefundNO();
                List<String> merchantNOList = new ArrayList<>();
                Map<Integer, List<Recharge>> adAccountIdMap = recharges.stream().collect(Collectors.groupingBy(
                        recharge -> recharge.getAdAccountId()
                ));

                Map<Integer, List<Recharge>> newmap = new HashMap<>();

                for (Map.Entry<Integer, List<Recharge>> entry : adAccountIdMap.entrySet()) {
                    List<Recharge> rechargeList = entry.getValue().stream().sorted(Comparator.comparing(Recharge::getPayTime).reversed())
                            .collect(Collectors.toList());
                    newmap.put(entry.getKey(), rechargeList);
                }
                for (Map.Entry<Integer, List<Recharge>> entry : newmap.entrySet()) {
                    Double areaAlreadyRefundMoney = 0d;
                    Integer areaId = entry.getKey();
                    Account account = accountService.getByAccountId(areaId);
                    List<Recharge> rechargeList = entry.getValue();
                    for (Recharge recharge : rechargeList) {
                        if (totalMoney == 0) {
                            break;
                        }
                        Double refundMoney = 0D;
                        if (totalMoney <= recharge.getCanRefundMoney()) {
                            refundMoney = totalMoney;
                            totalMoney = 0D;
                        } else {
                            refundMoney = recharge.getCanRefundMoney();
                            totalMoney = totalMoney - recharge.getCanRefundMoney();
                        }
                        /**生成退款订单号*/
                        String refundNO = OrderNoUtil.getRefundNO();
                        log.info(String.format("用户退款%s,订单编号%s,退款金额%s", userId, recharge.getOrderNO(), refundMoney));
                        double oldRefundMoney = refundMoney;
                        refundMoney = getReturnResult(account, recharge, refundNO, refundMoney, user, businessNO, operatorId, operatorName, operatorPhone, null, null);
                        if (refundMoney > 0) {
                            merchantNOList.add(recharge.getOrderNO());
                            User beforeUser = getByUserId(userId);
                            Long beforeUserMoney = beforeUser.getMoney().longValue();
                            updateMoney(userId, -refundMoney);
                            alreadyRefundMoney = alreadyRefundMoney + refundMoney;
                            areaAlreadyRefundMoney += refundMoney;
                            /**更改用户金额归属,汇付支付的独立计算，其他的全部算一类*/
                            if (recharge.getType().equals(RechargeConstant.RECHARGE_PAY_ADAPAY) || recharge
                                    .getType()
                                    .equals(RechargeConstant.RECHARGE_PAY_ADAPAY_ZFB)) {
                                userAdaPayMoneyService.add(user, 0, -refundMoney.intValue());
                            } else {
                                userAdaPayMoneyService.add(user, -refundMoney.intValue(), 0);
                            }
                            log.info("生成退款日志account={},user={},beforeUserMoney={},refundMoney={}", account, user, beforeUserMoney, refundMoney);
                            /**余额变动日志*/
                            BalanceChange balanceChange = new BalanceChange(user.getUserId(), user.getPhone(), user.getName()
                                    , account == null ? -1 : account.getAccountId(), user.getAccountId(), beforeUserMoney, refundMoney.longValue()
                                    , beforeUserMoney - refundMoney.longValue(), IncomeTypeContent.inComeRefund,
                                    BalanceLogChangeTypeContent.capitalType_balance, BalanceLogCapitalTypeContent.USER_REFUNDED_BALANCE
                                    , System.currentTimeMillis(), null, businessNO);
                            log.info("推送退款消息");
                            pushMsgLogService.sendPushLog(JSON.toJSONString(balanceChange), TableNameConstant.BALANCECHANGE);
                            /**记录退款日志*/
                            UserAccountLog userAccountLog = new UserAccountLog();
                            userAccountLog.setMoneyType(RechargeConstant.MONEY_TYPE_REAL);
                            userAccountLog.setAccountId(user.getAccountId());
                            userAccountLog.setMoney(refundMoney);
                            userAccountLog.setUserId(userId);
                            userAccountLog.setOperaTime(DateTimeUtil.getNowTime());
                            userAccountLog.setRemark("正常退款");
                            userAccountLog.setType(0);
                            log.info("生成退款日志完成");
                            userAccountLogService.insert(userAccountLog);
                            adaPayService.addRefundLog(recharge, areaId, refundNO, refundMoney, userId, recharge.getOrderNO());
                            log.info("插入退款日志完成");
                        } else {
                            log.info("退款失败,订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + ",继续下一笔退款");
                            totalMoney = totalMoney + (oldRefundMoney - refundMoney);
                            //throw new BaseException("-20015","金额退还失败，请联系客服");
                        }
                    }

                    Date now = new Date();
                    /** 推送退款日志 */
                    if (areaAlreadyRefundMoney > 0) {
                        String remark = null;
//					if(user.getMoney() > alreadyRefundMoney){
//						remark = "实际退款%s,人工充值金额退款失败%s";
//						remark = String.format(remark, alreadyRefundMoney + "", user.getMoney() - alreadyRefundMoney);
//
//					}
                        adaPayService.pushRefundLog(user, account, areaAlreadyRefundMoney, businessNO, merchantNOList, null,
                                RefundLogContent.balance_Refund, null, operatorId, operatorName, operatorPhone, null, now);

                        /**清除区域金额*/
                        UserAccountMoney giftCardMoney = userAccountMoneyService.getByAccountId(user.getUserId(), BalanceLogChangeTypeContent.capitalType_giftCard, areaId);
                        UserAccountMoney balanceMoney = userAccountMoneyService.getByAccountId(user.getUserId(), BalanceLogChangeTypeContent.capitalType_balance, areaId);
                        /**清除礼品卡余额*/
                        if (giftCardMoney != null) {
                            long giftCardStartMoney = giftCardMoney.getMoney();
                            if (giftCardStartMoney > 0) {
//							giftCardMoney.setMoney(-giftCardMoney.getMoney());
//							userAccountMoneyDao.edit(giftCardMoney);
//							BalanceChange giftCardBalanceChange = new BalanceChange(user.getUserId(),user.getPhone(), user.getName()
//									,account.getAccountId(), user.getAccountId(), giftCardStartMoney, giftCardStartMoney
//									,0L, IncomeTypeContent.inComeRefund,
//									BalanceLogChangeTypeContent.capitalType_giftCard, BalanceLogCapitalTypeContent.REFUND_BALANCE_MINUS_GIFT_CARD
//									, now.getTime(), "余额退款清除礼品卡余额",businessNO);
//							pushMsgLogService.sendPushLog(JSON.toJSONString(giftCardBalanceChange), TableNameConstant.BALANCECHANGE);
//							UserToAgent userToAgent = userToAgentDao.getByUserId(user.getUserId());
//							Integer originalAccountId = null;
//							if(userToAgent != null){
//								originalAccountId = userToAgent.getFirstId();
//							}
//							UserCapitalChange userCapitalChange =new UserCapitalChange(user.getUserId(),user.getPhone(),
//									user.getName(),account.getAccountId(),user.getAccountId(),giftCardStartMoney,giftCardStartMoney,
//									0L,PayPlatformContent.PAY_USE,PayPlatformContent.PAY_GIFCARD,BusinessConstant.BUSINESS_DEDUCTION_GIF
//									,null,operatorId,operatorPhone,operatorName,businessNO,null,now.getTime(),
//									"余额退款清除礼品卡余额", originalAccountId);
//							pushMsgLogService.sendPushLog(JSON.toJSONString(userCapitalChange), TableNameConstant.USERCAPITALCHANGE);
                            }
                        }
                        Double needRefundMoney = areaAlreadyRefundMoney;
                        /**清除预存款*/
                        if (balanceMoney != null) {
                            if (needRefundMoney >= balanceMoney.getMoney()) {
                                balanceMoney.setMoney(-balanceMoney.getMoney());
                                needRefundMoney = needRefundMoney - balanceMoney.getMoney();
                                userAccountMoneyDao.edit(balanceMoney);
                            } else {
                                balanceMoney.setMoney(-needRefundMoney.longValue());
                                needRefundMoney = 0D;
                                userAccountMoneyDao.edit(balanceMoney);
                            }
                        }
                        if (needRefundMoney > 0) {
                            List<UserAccountMoney> userAccountMonies = userAccountMoneyService.getByUserIdAndBrandId(userId, BalanceLogChangeTypeContent.capitalType_balance);
                            for (UserAccountMoney userAccountMoney : userAccountMonies) {
                                if (userAccountMoney.getMoney() == 0) {
                                    continue;
                                }
                                if (needRefundMoney > userAccountMoney.getMoney()) {
                                    needRefundMoney = needRefundMoney - userAccountMoney.getMoney();
                                    userAccountMoney.setMoney(-userAccountMoney.getMoney());
                                    userAccountMoneyDao.edit(userAccountMoney);
                                } else {
                                    userAccountMoney.setMoney(-needRefundMoney.longValue());
                                    userAccountMoneyDao.edit(userAccountMoney);
                                    break;
                                }
                            }
                        }

                        /**查询用户是否存在礼品卡余额*/
                        GiftCardUser giftCardUser = giftCardUserService.getByUserId(user.getUserId(), areaId);
                        log.info("退款查询礼品卡信息：userId=" + user.getUserId() + ",adAccountId=" + areaId);
                        if (giftCardUser != null && giftCardUser.getMoney() > 0) {
                            long giftCardStartMoney = giftCardUser.getMoney();
                            giftCardUser.setMoney(-giftCardUser.getMoney());
                            giftCardUser.setUpdateTime(DateTimeUtil.DateTimeToString(now));
                            giftCardUserService.insert(giftCardUser, "余额退款清除礼品卡余额",
                                    BalanceLogCapitalTypeContent.REFUND_BALANCE_MINUS_GIFT_CARD, businessNO, DateTimeUtil.DateTimeToString(now));
                            log.info("退款清除礼品卡余额=" + giftCardStartMoney);
                        }
                    }
                }

                /**成功退款金额小于总的退款金额，返回提示*/
                if (alreadyRefundMoney < money) {
                    throw new BaseException("-20022", "部分余额退还失败，请联系客服人工处理");
                }
            } else {
                throw new BaseException("-20017", "该区域未开启自动退款，请联系客服人工处理");
            }
            return alreadyRefundMoney;
        } finally {
            redisService.del(RedisConstant.REFUND_LOCK + userId);
        }

    }

    @Override
    public Integer getPayPlatformTypeByRecharge(Recharge recharge) {
        if (recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_APP) || recharge.getType()
                .equals(RechargeConstant.RECHARGE_WEIXIN_XCX) ||
                recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_AFF) ||
                recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_ZK_XCX)) {
            return PayPlatformContent.WeChat;
        }
        if (recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_APP) ||
                recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_XCX)
                || recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_ZK_ZFB)) {
            return PayPlatformContent.AliPay;
        }
        if (recharge.getType().equals(RechargeConstant.RECHARGE_PAY_ADAPAY) || recharge
                .getType()
                .equals(RechargeConstant.RECHARGE_PAY_ADAPAY_ZFB)) {
            return PayPlatformContent.HuiFu;
        }
        return null;
    }

    /**
     * 退款逻辑实现
     *
     * @param recharge    订单
     * @param refundNO    退款订单号
     * @param refundMoney 退款金额
     * @param user        用户信息
     * @return
     */
    @Override
    public Double getReturnResult(Account account, Recharge recharge, String refundNO, Double refundMoney, User user, String refundBusinessNO,
                                  Integer operatorId, String operatorName, String operatorPhone, String rideOrderNo, String transactionLogRemark) {
        boolean isSucc = false;
        if (recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_APP) || recharge.getType()
                .equals(RechargeConstant.RECHARGE_WEIXIN_XCX)) {
            /**获取平台微信支付信息*/
            //WeixinPayConfig weixinPayConfig=weixinPayConfigService.getByAccountId(user.getAccountId(),recharge.getType());


            // 查询用户使用的小程序appid
            WeixinPayConfig brandWeixinPayConfig = weixinPayConfigService.getByAccountId(user.getAccountId(), recharge.getType());
            String appId = brandWeixinPayConfig.getAppId();

            WeixinPayConfig weixinPayConfig = null;
            /**1.根据订单号查询区域充值表获取支付配置*/
            RechargeMerchant rechargeMerchant = rechargeMerchantService.getByOrderNO(recharge.getOrderNO());
            if (null != rechargeMerchant) {
                weixinPayConfig = weixinPayConfigService.getByPartnerAndAppId(rechargeMerchant.getPartner(),appId,false);//商户id，区域id，品牌id
            }
            if (weixinPayConfig == null) {
                /**2.根据订单号没获取到区域充值表配置的，默认获取平台微信支付配置*/
                weixinPayConfig = weixinPayConfigService.getByAccountId(user.getAccountId(), recharge.getType());
            } else {
                log.info("充值订单号=" + recharge.getOrderNO() + ",充值商户表获取的商户=" + weixinPayConfig.getPartner());
            }
            log.info("订单为微信app或小程序充值，退款单号{}，退款金额{}，订单金额{}", refundNO, refundMoney, recharge.getMoney());
            //                String wxMchPay = cacheService.getBrandRemoteConfigValue(accountId, BrandConstant.WX_MCH_PAY_SWITCH);
            // 根据支付订单号判断订单支付的时候是否使用微信服务商支付
            RechargePayType one = rechargePayTypeDao.getOne(recharge.getOrderNO());
            boolean isMchPay = false;
            // 非空且为微信服务商支付
            if (Objects.nonNull(one) && one.getType() == 1){
                isMchPay = true;
            }
            /**调用微信退款接口*/
            isSucc = WeixinPay.returnMoney(weixinPayConfig, recharge.getOrderNO(), refundNO, String.valueOf(refundMoney.intValue()), String.valueOf(recharge.getMoney().intValue()), isMchPay);
            if (isSucc) {
                addRideUseLog(rideOrderNo, recharge, refundMoney, weixinPayConfig.getPartner());
                // 退款调用成功进行主动查询退款订单状态
                String payApiType = isMchPay ? PayApiTypeEnum.WECHAT_MCH_V3.getApiType() : PayApiTypeEnum.WECHAT_V2.getApiType();
                weixinPayService.sendPayMsg(refundNO, RechargeConstant.PAY_REFUND, payApiType);
                adaPayService.pushRefundDetailLog(user, account, refundMoney, refundNO, refundBusinessNO, recharge, operatorId, operatorName, operatorPhone, null, rideOrderNo, transactionLogRemark, null);
                return refundMoney;
            }
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + isSucc);
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_APP)) {
            /**获取平台支付宝支付信息*/ /**支付宝app*/
            ZfbPayConfig zfbPayConfig = zfbPayConfigService.getByAccountId(user.getAccountId(), RechargeConstant.ZFB_PAY_APP);
            log.info("订单为支付宝app充值，退款单号{}，退款金额{}，订单金额{}", refundNO, refundMoney, recharge.getMoney());
            isSucc = ZFBReturn.returnMoneyNew(zfbPayConfig, recharge.getOrderNO(), refundNO, refundMoney);
            if (isSucc) {
                addRideUseLog(rideOrderNo, recharge, refundMoney, zfbPayConfig.getPartner());
                weixinPayService.sendPayMsg(refundNO, RechargeConstant.PAY_REFUND,null);
                adaPayService.pushRefundDetailLog(user, account, refundMoney, refundNO, refundBusinessNO, recharge, operatorId, operatorName, operatorPhone, null, rideOrderNo, transactionLogRemark, null);
                return refundMoney;
            }
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + isSucc);
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_XCX)) {
            /**获取平台支付宝支付信息*/ /**支付宝小程序*/
            ZfbPayConfig zfbPayConfig = zfbPayConfigService.getByAccountId(user.getAccountId(), RechargeConstant.ZFB_PAY_XCX);
            log.info("订单为支付宝小程序充值，退款单号{}，退款金额{}，订单金额{}", refundNO, refundMoney, recharge.getMoney());
            isSucc = ZFBReturn.returnMoneyNew(zfbPayConfig, recharge.getOrderNO(), refundNO, refundMoney);
            if (isSucc) {
                addRideUseLog(rideOrderNo, recharge, refundMoney, zfbPayConfig.getPartner());
                weixinPayService.sendPayMsg(refundNO, RechargeConstant.PAY_REFUND,null);
                adaPayService.pushRefundDetailLog(user, account, refundMoney, refundNO, refundBusinessNO, recharge, operatorId, operatorName, operatorPhone, null, rideOrderNo, transactionLogRemark, null);
                return refundMoney;
            }
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + isSucc);
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_PAY_ADAPAY) || recharge
                .getType()
                .equals(RechargeConstant.RECHARGE_PAY_ADAPAY_ZFB)) {
            /**汇通天下聚合支付*/
            AdaPayConfig adaPayConfig = cacheService.getAdaPayConfig(user.getAccountId(),
                    AdaPayConstant.PAY_CHANNEL_WXXCX);
            log.info("订单为汇付充值，退款单号{}，退款金额{}，订单金额{}", refundNO, refundMoney, recharge.getMoney());
            /**查询聚合支付订单*/
            AdaPayLog adaPayLog = adaPayLogService.getByOrderNO(recharge.getOrderNO());
            if (adaPayLog == null) {
                return 0d;
            }
            if (adaPayLog.getState().equals(AdaPayConstant.ADAPAY_LOG_REFUND) || adaPayLog.getState().equals(
                    AdaPayConstant.ADAPAY_LOG_DEFAULT)) {
                return 0d;
            }
            /**已支付订单和部分退款订单,需调用支付撤销接口*/
            //double realRefundMoney= adaPayService.refundMoney(refundMoney,user.getUserId(),null,adaPayConfig);
            boolean isCashAll = adaPayService.getPayType(adaPayConfig.getAccountId());
            double realRefundMoney = adaPayService.refund(account, recharge, adaPayConfig, isCashAll, user, refundMoney, refundNO, refundBusinessNO,
                    operatorId, operatorName, operatorPhone, rideOrderNo, transactionLogRemark);
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + ",实际退款金额=" + realRefundMoney);
            if (realRefundMoney > 0) {
                isSucc = true;

                refundMoney = realRefundMoney;
            }
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + isSucc);
            if (realRefundMoney > 0) {
                return refundMoney;
            }

            if (isSucc) {
                addRideUseLog(rideOrderNo, recharge, refundMoney, adaPayConfig.getAppId());
            }

			/*if(isSucc){
				AdaPayLog adaPayLogDB = new AdaPayLog();
				adaPayLogDB.setAdaPayId(adaPayLog.getAdaPayId());
				adaPayLogDB.setRefundNO(adaPayLog.getRefundNO()==null?refundNO:adaPayLog.getRefundNO()+";"+refundNO);
				adaPayLogDB.setRefundMoney(adaPayLog.getRefundMoney()+refundMoney);
				if((adaPayLog.getRefundMoney()+refundMoney)==adaPayLog.getMoney()){
					adaPayLogDB.setState(AdaPayConstant.ADAPAY_LOG_REFUND);
				}else{
					adaPayLogDB.setState(adaPayLog.getState());
				}
				adaPayLogService.editRefund(adaPayLogDB);

			}*/

        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_AFF)) {
            /**微信支付分退款*/
            log.info("订单为微信支付分充值，退款单号{}，退款金额{}，订单金额{}", refundNO, refundMoney, recharge.getMoney());
            isSucc = payPointService.refund(recharge.getOrderNO(), "骑行订单退款", refundMoney.intValue(), user, refundNO);
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + isSucc);
            if (isSucc) {
                weixinPayService.sendPayMsg(refundNO, RechargeConstant.PAY_REFUND,null);
                adaPayService.pushRefundDetailLog(user, account, refundMoney, refundNO, refundBusinessNO, recharge, operatorId, operatorName, operatorPhone, null, rideOrderNo, transactionLogRemark, null);
                return refundMoney;
            }
        }
        return 0d;
    }


    /**
     * 退款逻辑实现(新)
     *
     * @param recharge    订单
     * @param refundNO    退款订单号
     * @param refundMoney 退款金额
     * @param user        用户信息
     * @return
     */
    @Override
    public Double getReturnResultNew(Account account, Recharge recharge, String refundNO, Double refundMoney, User user, String refundBusinessNO,
                                     Integer operatorId, String operatorName, String operatorPhone, String rideOrderNo, String transactionLogRemark, Integer sendMsg, boolean isApi, String refundBatch) {
        boolean isSucc = false;
        if (recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_APP) || recharge.getType()
                .equals(RechargeConstant.RECHARGE_WEIXIN_XCX)) {
            log.info("退款订单号={}，充值订单类型为微信app/小程序", refundNO);
            /**获取平台微信支付信息*/
            //WeixinPayConfig weixinPayConfig=weixinPayConfigService.getByAccountId(user.getAccountId(),recharge.getType());

            // 查询用户使用的小程序appid
            WeixinPayConfig brandWeixinPayConfig = weixinPayConfigService.getByAccountId(user.getAccountId(), recharge.getType());
            String appId = brandWeixinPayConfig.getAppId();

            WeixinPayConfig weixinPayConfig = null;
            /**1.根据订单号查询区域充值表获取支付配置*/
            RechargeMerchant rechargeMerchant = rechargeMerchantService.getByOrderNO(recharge.getOrderNO());
            if (null != rechargeMerchant) {
                weixinPayConfig = weixinPayConfigService.getByPartnerAndAppId(rechargeMerchant.getPartner(),appId,false);//商户id，区域id，品牌id
            }
            if (weixinPayConfig == null) {
                /**2.根据订单号没获取到区域充值表配置的，默认获取平台微信支付配置*/
                weixinPayConfig = weixinPayConfigService.getByAccountId(user.getAccountId(), recharge.getType());
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",充值商户表获取的商户=" + weixinPayConfig.getPartner());
            }
            // String wxMchPay = cacheService.getBrandRemoteConfigValue(accountId, BrandConstant.WX_MCH_PAY_SWITCH);
            // 根据支付订单号判断订单支付的时候是否使用微信服务商支付
            // 根据支付订单号判断订单支付的时候是否使用微信服务商支付
            RechargePayType one = rechargePayTypeDao.getOne(recharge.getOrderNO());
            boolean isMchPay = false;
            // 非空且为微信服务商支付
            if (Objects.nonNull(one) && one.getType() == 1){
                isMchPay = true;
            }
            /**调用微信退款接口*/
            String errorMsg=null;
            try {
                isSucc = WeixinPay.returnMoney(weixinPayConfig, recharge.getOrderNO(), refundNO, String.valueOf(refundMoney.intValue()), String.valueOf(recharge.getMoney().intValue()), isMchPay);
            }catch (Exception e){
                errorMsg = e.getMessage();
            }
            if (isSucc) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "同步退款调用结果=" + true);
                RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(),
                        RefundConstant.REFUND_PENDING, false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark,
                        sendMsg, isApi, null, operatorId, operatorName, operatorPhone, rideOrderNo, RefundConstant.LOG3 + ",", null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null,null,null,null);
                refundOrderDao.insert(refundOrder);
                // 退款调用成功进行主动查询退款订单状态
                String payApiType = isMchPay ? PayApiTypeEnum.WECHAT_MCH_V3.getApiType() : PayApiTypeEnum.WECHAT_V2.getApiType();
                weixinPayService.sendPayMsg(refundNO, RechargeConstant.PAY_REFUND, payApiType);
                return refundMoney;
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_FAIL,
                        false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName,
                        operatorPhone, rideOrderNo, null, null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null,null,null,errorMsg);
                refundOrderDao.insert(refundOrder);
            }
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_APP)) {
            log.info("退款订单号={}，充值订单类型为支付宝app", refundNO);
            /**获取平台支付宝支付信息*/ /**支付宝app*/
            ZfbPayConfig zfbPayConfig = zfbPayConfigService.getByAccountId(user.getAccountId(), RechargeConstant.ZFB_PAY_APP);
            RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_PENDING,
                    false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId,
                    operatorName, operatorPhone, rideOrderNo, RefundConstant.LOG4 + ",", null, null, null,
                    account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                    null,null,null,null);
            refundOrderDao.insert(refundOrder);
            isSucc = ZFBReturn.returnMoneyNew(zfbPayConfig, recharge.getOrderNO(), refundNO, refundMoney);
            if (isSucc) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "同步退款调用结果=" + true);
                return refundMoney;
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                // 更新
                refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_FAIL,
                        false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName,
                        operatorPhone, rideOrderNo, null, null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null,null,null,null);
                refundOrderDao.update(refundOrder);
            }
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_ZFB_XCX)) {
            log.info("退款订单号={}，充值订单类型为支付宝小程序", refundNO);
            /**获取平台支付宝支付信息*/ /**支付宝小程序*/
            ZfbPayConfig zfbPayConfig = zfbPayConfigService.getByAccountId(user.getAccountId(), RechargeConstant.ZFB_PAY_XCX);
            RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_PENDING,
                    false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName,
                    operatorPhone, rideOrderNo, RefundConstant.LOG5 + ",", null, null, null,
                    account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                    null,null,null,null);
            refundOrderDao.insert(refundOrder);
            isSucc = ZFBReturn.returnMoneyNew(zfbPayConfig, recharge.getOrderNO(), refundNO, refundMoney);
            if (isSucc) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "同步退款调用结果=" + true);
                return refundMoney;
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                // 更新
                refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_FAIL, false,
                        recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName, operatorPhone,
                        rideOrderNo, null, null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null,null,null,null);
                refundOrderDao.update(refundOrder);
            }
        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_PAY_ADAPAY) || recharge
                .getType()
                .equals(RechargeConstant.RECHARGE_PAY_ADAPAY_ZFB)) {
            log.info("退款订单号={}，充值订单类型为汇付充值/汇付支付宝", refundNO);
            /**汇通天下聚合支付*/
            AdaPayConfig adaPayConfig = cacheService.getAdaPayConfig(user.getAccountId(),
                    AdaPayConstant.PAY_CHANNEL_WXXCX);
            /**查询聚合支付订单*/
            AdaPayLog adaPayLog = adaPayLogService.getByOrderNO(recharge.getOrderNO());
            if (adaPayLog == null) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                return 0d;
            }
            if (adaPayLog.getState().equals(AdaPayConstant.ADAPAY_LOG_REFUND) || adaPayLog.getState().equals(
                    AdaPayConstant.ADAPAY_LOG_DEFAULT)) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                return 0d;
            }
            /**已支付订单和部分退款订单,需调用支付撤销接口*/
            //double realRefundMoney= adaPayService.refundMoney(refundMoney,user.getUserId(),null,adaPayConfig);
            boolean isCashAll = adaPayService.getPayType(adaPayConfig.getAccountId());
            // 退款订单记录提到了内层调用
            double realRefundMoney = adaPayService.refundNew(account, recharge, adaPayConfig, isCashAll, user, refundMoney, refundNO, refundBusinessNO,
                    operatorId, operatorName, operatorPhone, rideOrderNo, transactionLogRemark, sendMsg, isApi, refundBatch);
            log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + ",实际退款金额=" + realRefundMoney);
            if (realRefundMoney > 0) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "同步退款调用结果=" + true);
                refundMoney = realRefundMoney;
                RefundOrder refundOrder = refundOrderDao.getByRefundNO(refundNO);
                if (Objects.isNull(refundOrder)) {
                    log.error("refundorder为空，请排查refundorder插入问题,refundNO={}", refundNO);
                    return refundMoney;
                }
                String logTag = Objects.isNull(refundOrder.getLogTag()) ? (RefundConstant.LOG6 + ",") : (refundOrder.getLogTag() + RefundConstant.LOG6 + ",");
                refundOrder.setLogTag(logTag);
                refundOrder.setRideOrderNO(rideOrderNo);
                refundOrder.setRechargeNO(recharge.getOrderNO());
                refundOrder.setRefundMoney(refundMoney);
                refundOrderDao.update(refundOrder);
                return refundMoney;
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
            }

        } else if (recharge.getType().equals(RechargeConstant.RECHARGE_WEIXIN_AFF)) {
            log.info("退款订单号={}，充值订单类型为微信支付分", refundNO);
            /**微信支付分退款*/
            String errorMsg = null;
            try{
                isSucc = payPointService.refund(recharge.getOrderNO(), "骑行订单退款", refundMoney.intValue(), user, refundNO);
            } catch (Exception e){
                errorMsg = e.getMessage();
            }
            if (isSucc) {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "同步退款调用结果=" + true);
                RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_PENDING,
                        false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName,
                        operatorPhone, rideOrderNo, RefundConstant.LOG7 + ",", null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null, null, null, null);
                refundOrderDao.insert(refundOrder);
                return refundMoney;
            } else {
                log.info("订单号=" + recharge.getOrderNO() + ",退款金额=" + refundMoney + "退款结果=" + false);
                RefundOrder refundOrder = new RefundOrder(refundBusinessNO, refundNO, refundMoney, refundMoney, DateTimeUtil.getNowTime(), RefundConstant.REFUND_FAIL,
                        false, recharge.getOrderNO(), null, null, null, false, null, transactionLogRemark, sendMsg, isApi, null, operatorId, operatorName, operatorPhone,
                        rideOrderNo, null, null, null, null,
                        account.getAccountId(), 0d, 0d, null, user.getUserId(), transactionLogRemark, null, null, 0d, 0d,
                        null, null, null, errorMsg);
                refundOrderDao.insert(refundOrder);
            }
        }
        return 0d;
    }


    public void addRideUseLog(String orderNO, Recharge recharge, Double refundMoney, String partner) {
        RideUseLog rideUseLog = rideUseLogService.getByOrderNO(orderNO);
        if (null != rideUseLog) {
            RideUseRefundLog rideUseRefundLog = new RideUseRefundLog();
            rideUseRefundLog.setAccountId(recharge.getBrandId());
            rideUseRefundLog.setAdAccountId(recharge.getAdAccountId());
            rideUseRefundLog.setOrderNO(rideUseLog.getOrderNO());
            rideUseRefundLog.setAddTime(new Date());
            rideUseRefundLog.setRechargeOrderNO(recharge.getOrderNO());
            rideUseRefundLog.setPartner(partner);
            switch (recharge.getRechargeType()) {
                case 3:
                    /**充值结束订单**/
                    Double otherRefund = rideUseLog.getOtherRefund() + refundMoney;
                    log.info("充值订单号=" + recharge.getOrderNO() + ",骑行订单号=" + rideUseLog.getOrderNO() + ",3更新其他退款=" + otherRefund);
                    rideUseLog.setOtherRefund(otherRefund.intValue());//其他退款
                    rideUseLogService.update(rideUseLog);
                    rideUseRefundLog.setBalanceRefund(0);
                    rideUseRefundLog.setOtherRefund(refundMoney.intValue());
                    rideUseRefundLogDao.insert(rideUseRefundLog);
                    break;
                case 0:
                    /**押金和充值**/
                    Double balanceRefund = rideUseLog.getBalanceRefund() + refundMoney;
                    log.info("充值订单号=" + recharge.getOrderNO() + ",骑行订单号=" + rideUseLog.getOrderNO() + ",1更新余额退款=" + balanceRefund);
                    rideUseLog.setBalanceRefund(balanceRefund.intValue());//余额退款
                    rideUseLogService.update(rideUseLog);
                    rideUseRefundLog.setBalanceRefund(refundMoney.intValue());
                    rideUseRefundLog.setOtherRefund(0);
                    rideUseRefundLogDao.insert(rideUseRefundLog);
                    break;
                default:
            }
        }
    }

    @Override
    public void updateRideMoney(User user) {
        userDao.updateRideMoney(user);
    }

    @Override
    public double returnMoneyApi(Integer userId, Double money, Long fineMoney, String buyOrderNO, String sign, Long date,
                                 String param, Integer adAccountId, Integer operatorId, String operatorName,
                                 String operatorPhone, Integer sendMsg, String remark) {

        boolean result = MD5PasswordUtil.signCheck(sign, date, param);
        if (!result) {
            throw new BaseException("-190002", "签名校验失败");
        }
        User user = userDao.getByUserId(userId);
        if (user == null) {
            throw new BaseException("-20005", "用户不存在");
        }
        /**汇通天下聚合支付*/
        AdaPayConfig adaPayConfig = cacheService.getAdaPayConfig(user.getAccountId(),
                AdaPayConstant.PAY_CHANNEL_WXXCX);
		/*if(adaPayConfig==null){
			throw new BaseException("-4005","聚合支付未配置信息");
		}*/
        if (adAccountId == null) {
            adAccountId = user.getAccountId();
        }
        double refundMoney = adaPayService.refundMoney(money, fineMoney, userId, buyOrderNO, adaPayConfig, adAccountId,
                operatorId, operatorName, operatorPhone, sendMsg, remark);
        refundMoney = DoubleUtil.parseDouble(refundMoney);
        return refundMoney;
    }

    @Override
    public RefundMoneyReturnDTO returnMoneyApiNew(Integer userId, Double money, Long fineMoney, String buyOrderNO, String sign, Long date,
                                                  String param, Integer adAccountId, Integer operatorId, String operatorName,
                                                  String operatorPhone, Integer sendMsg, String remark) {

        boolean result = MD5PasswordUtil.signCheck(sign, date, param);
        if (!result) {
            throw new BaseException("-190002", "签名校验失败");
        }
        User user = userDao.getByUserId(userId);
        if (user == null) {
            throw new BaseException("-20005", "用户不存在");
        }
        /**汇通天下聚合支付*/
        AdaPayConfig adaPayConfig = cacheService.getAdaPayConfig(user.getAccountId(),
                AdaPayConstant.PAY_CHANNEL_WXXCX);
		/*if(adaPayConfig==null){
			throw new BaseException("-4005","聚合支付未配置信息");
		}*/
        if (adAccountId == null) {
            adAccountId = user.getAccountId();
        }
        RefundMoneyReturnDTO refundMoneyReturnDTO = adaPayService.refundMoneyNew(money, fineMoney, userId, buyOrderNO, adaPayConfig, adAccountId,
                operatorId, operatorName, operatorPhone, sendMsg, remark);
        refundMoneyReturnDTO.setRefundMoney(DoubleUtil.parseDouble(refundMoneyReturnDTO.getRefundMoney()));
        return refundMoneyReturnDTO;
    }

    /**
     * 对用户退款操作进行加锁
     *
     * @param userId 用户id
     */
    @Override
    public void lockUserRefund(Integer userId) {
        //限制用户跨天退款，23：59：45秒到00：00：05秒之间发起退款
        int hms = DateTimeUtil.getHms(new Date());
        //大于23：59：45秒
        if (hms > 235945) {
            throw new BaseException(String.format("系统对账中，请%s秒后再申请退款", 235965 - hms));
        }
        //小于00:00:05秒
        if (hms < 5) {
            throw new BaseException(String.format("系统对账中，请%s秒后再申请退款", 5 - hms));
        }
        if (!redisService.setNx(RedisConstant.REFUND_USER_LOCK + userId, RedisConstant.REFUND_USER_LOCK, 10)) {
            throw new BaseException("-3070", "退款中,请耐心等待");
        }
    }

    @Override
    public void unlockUserRefund(Integer userId) {
        redisService.del(RedisConstant.REFUND_USER_LOCK + userId);
    }

    @Override
    public DetectAuthResponse youquFaceCheckLocal(User user, String userCode, Integer accountId) {
        Machine machine = machineCheck(userCode);
        FaceCheck faceCheck = faceTimesCheck(user.getUserId());
        DetectAuthResponse detectAuthResponse = new DetectAuthResponse();
        //获取优驱api配置并且判断人脸识别是否是优驱api
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(), BrandConstant.TBIT_CONFIG);

        if (!jsonObject.isEmpty()) {
            AccountConfigExt accountConfigExt = cacheService.selectByAccountIdAndKey(machine.getAccountId(), AccountConfigExtConstant.FACE_RECOGNITION_CONFIG);
            //设定为yq人脸识别自研为1111
            if (AccountConfigExtConstant.yqFace.equals(accountConfigExt.getParamValue())) {
                String appId = jsonObject.getString("appId");
                String nonce = jsonObject.getString("nonce");
                String appSecret = jsonObject.getString("appSecret");
                User userDB = getUser(user.getUserId());
                UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
                String faceH5Url = "";
                Map<String, Object> bizToken = TbitHttpServiceUtil.getTokenLocal(tbitProperties.getUrl(), appId, nonce, appSecret, faceH5Url, Objects.isNull(userFaceData) ? null : userFaceData.getUrl(), userDB.getName(), userDB.getIdNO());
                if (Objects.isNull(bizToken.get("bizToken")) || Objects.isNull(bizToken.get("url"))) {
                    if (bizToken.isEmpty()) {
                        throw new BaseException("人脸识别失败,请联系客服为您处理");
                    }
                    log.error("优驱人脸识别自研返回参数为空:{}", JSON.toJSONString(bizToken));
                    throw new BaseException((String) bizToken.get("msg"));
                }
                detectAuthResponse.setBizToken(bizToken.get("bizToken").toString());
                detectAuthResponse.setUrl(bizToken.get("url").toString());
                return detectAuthResponse;
            }
        }
        return detectAuthResponse;
    }

    /**
     * 身份证+人脸识别实名认证
     */
    @Override
    public DetectAuthResponse nameAuthGetBizToken(User user, Integer accountId, String redirectUrl, String userName, String idNO) {
        log.info("开始身份证+人脸识别实名认证:{}-{}-{}-{}",accountId, userName, idNO, redirectUrl);
        redisService.add(RedisConstant.AUTH_FACE + user.getUserId(), String.format("%s-%s", userName, idNO), 60 * 30 );
        checkNameAuth(user.getUserId(), user.getAccountId(), accountId, userName, idNO);
        //校验该区域是否打开实名认证+人脸识别
        AccountConfigExt accountConfigExtByKey = cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.NAME_AUTH_TYPE);
        if (Objects.isNull(accountConfigExtByKey) || !accountConfigExtByKey.getParamValue().equals("2")){
            throw new BaseException("系统未配置实名认证+人脸识别");
        }
        //获取优驱api配置
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(), BrandConstant.TBIT_CONFIG);
        if (Objects.isNull(jsonObject)){
            throw new BaseException("实名认证配置异常，请您联系客服处理");
        }
        //腾讯云人脸识别
        Map<String, Object> bizToken = TbitHttpServiceUtil.getToken(jsonObject.getString("appId"), jsonObject.getString("nonce"), jsonObject.getString("appSecret"), redirectUrl, null, userName, idNO);
        DetectAuthResponse detectAuthResponse = new DetectAuthResponse();
        if (Objects.isNull(bizToken.get("bizToken")) || Objects.isNull(bizToken.get("url")) || Objects.isNull(bizToken.get("requestId"))) {
            if (bizToken.isEmpty()) {
                throw new BaseException("人脸识别失败,请联系客服为您处理");
            }
            log.error("优驱腾讯云人脸识别返回参数异常:{}", JSON.toJSONString(bizToken));
            throw new BaseException("优驱腾讯人脸获取bizToken异常，请稍后再试");
        }
        detectAuthResponse.setBizToken(bizToken.get("bizToken").toString());
        detectAuthResponse.setUrl(bizToken.get("url").toString());
        detectAuthResponse.setRequestId(bizToken.get("requestId").toString());
        return detectAuthResponse;
    }


    /**
     * 身份证 + 腾讯人脸识别获取结果
     * @param user 用户信息
     * @param bizToken bizToken
     * @param accountId accountId
     * @return result
     */
    @Override
    public Boolean nameAuthGetResult(User user, String bizToken, Integer accountId) {
        //校验该区域是否打开实名认证+人脸识别
        AccountConfigExt accountConfigExtByKey = cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.NAME_AUTH_TYPE);
        if (Objects.isNull(accountConfigExtByKey) || !accountConfigExtByKey.getParamValue().equals("2")){
            throw new BaseException("系统未配置实名认证+人脸识别");
        }
         //获取优驱api配置并且判断人脸识别是否是优驱api
        JSONObject jsonObject = cacheService.getBrandJsonConfig(user.getAccountId(), BrandConstant.TBIT_CONFIG);
         if (Objects.isNull(jsonObject)){
             throw new BaseException("实名认证配置异常，请您联系客服处理");
        }

        //获取oss人脸识别图片
        OssConfig ossConfig = cacheService.getOssConfig(user.getAccountId(), "gx-face");

        UserFaceData userFaceData = userFaceDataDao.getByUserId(user.getUserId());
        //调用优驱api腾讯云人脸识别结果
        Map<String, Object> result = TbitHttpServiceUtil.getResult(jsonObject.getString("appId"), jsonObject.getString("nonce"), jsonObject.getString("appSecret"), bizToken, Objects.isNull(userFaceData) ? null : userFaceData.getUrl());
        String bestFrame = result.get("bestFrame") == null ? null : result.get("bestFrame").toString();
        if (CollectionUtil.isNotEmpty(result)) {
            if (Objects.nonNull(result.get("result")) && "true".equals(result.get("result"))) {
                redisService.add(RedisConstant.REDIS_USER_FACE + user.getUserId(), "1", accountId);
                //识别结果为成功才存储本地缓存
                try {
                    if (userFaceData == null) {
                        Object sim = result.get("sim");
                        addLocalFace(user, Objects.nonNull(bestFrame) ? bestFrame : null, ossConfig, Objects.nonNull(sim) ? Double.parseDouble(sim.toString()) : 0D);
                    }
                } catch (Exception e) {
                    log.error("身份证 + 腾讯人脸识别获取结果异常:", e);
                }
                double sim = Double.parseDouble(result.getOrDefault("sim", "0").toString());
                userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame, SysConstant.USER_FACE_SUCCESS, bizToken, sim / 100, "优驱腾讯");
                //实名认证信息
                try {
                    String s = redisService.get(RedisConstant.AUTH_FACE + user.getUserId());
                    if (StrUtil.isNotEmpty(s)){
                        log.info("[身份证+人脸识别实名认证] 用户{} 姓名身份证号码{} 进行实名认证", user.getUserId(), s);
                        String[] split = s.split("-");
                        updateNameAuth(user.getUserId(),split[1] , split[0]);
                        /**兼容根据身份证设置性别生日*/
                        userDao.editSex(user.getUserId());
                        /**兼容用户参与活动记录用户名称*/
                        userRewardLogService.editName(split[0], user.getUserId());
                    }
                }catch (Exception e){
                    log.error("身份证 + 腾讯人脸识别 实名认证异常:", e);
                }
                return true;
            }
            double sim = Double.parseDouble(result.getOrDefault("sim", "0").toString());
            userFaceLogService.insertUserFaceLogAndUpdate(user, bestFrame, SysConstant.USER_FACE_NOT_SUCCESS, bizToken, sim / 100, "优驱腾讯");
        }

        return false;
    }


    /**
     * 骑行中位置显示
     * @param userCode 车辆编号
     * @return 类型
     */
    @Override
    public Integer ridingPositionDisplay(String userCode) {
        Machine machine = machineDao.getByUserCode(userCode);
        if (Objects.isNull(machine)){
            throw new BaseException("-4006","车辆编号不存在");
        }

        //获取当前车辆位置
        MachineStatus machineStatus = machineStatusService.getByMachineNO(machine.getMachineNO(), MapConstant.MAP_GOOGLE);
        if (Objects.isNull(machineStatus)) {
            throw new BaseException("获取位置信息失败");
        }
        Double lon = machineStatus.getLon();
        Double lat = machineStatus.getLat();

        //判断当前经纬度与历史经纬度是否一致
        String location = redisService.get(RedisConstant.MACHINE_RIDING_LOCATION + userCode);
        if (StrUtil.isNotEmpty(location)){
            String[] typeString = location.split("-");
            String s = typeString[0];
            String[] split = s.split(",");
            if (lon == Double.parseDouble(split[0]) && lat == Double.parseDouble(split[1])){
                return Integer.parseInt(typeString[1]);
            }
        }

        //是否开启禁停区模式
        Integer type = null;
        AccountConfig config = cacheService.getConfig(machine.getAccountId());
        type = closeNoParkArea(machine.getAccountId(), lon, lat, config.getReturnModel());
        String value = String.format("%s,%s-%s", lon, lat, type);
        redisService.add(RedisConstant.MACHINE_RIDING_LOCATION + userCode, value);
        return type;
    }

    @Override
    public boolean checkArrearsByIdNO(Integer accountId, String idNO) {
        return userDao.checkArrearsByIdNO(accountId, idNO);
    }

    /**
     * 站点模式计算逻辑
     * returnModel 0:站点模式 1：禁停区模式
     * @return 类型
     */
    private Integer closeNoParkArea(Integer accountId, Double lon, Double lat, Integer returnModel) {

        //校验是否在围栏内
        List<Geo> geo = cacheService.getGeo(accountId);
        List<Point> geoPoints = GeoUtil.getPoints(geo.get(0).getPoints());
        if (!GeoUtil.IsPtInPoly(new Point(lon, lat), geoPoints)) {
            return RidingPositionConstant.OUT_AREA;
        }
        //校验是否在围栏附近
        if (GeoUtil.getDistance(new Point(lon, lat), geoPoints) <= 50) {
            return RidingPositionConstant.NEAR_OUT_AREA;
        }
        //校验站点
        List<ParkPoint> parkPointList = parkPointService.getByArea(accountId, lon, lat, 100);
        for (ParkPoint parkPoint : parkPointList) {
            //判断是否在站点内
            if (GeoUtil.IsPtInPoly(new Point(lon, lat), GeoUtil.getPoints(parkPoint.getPoints()))) {
                return RidingPositionConstant.IS_PARK_POINT;
            }
            if (returnModel == 0 && GeoUtil.getDistance(new Point(lon, lat), GeoUtil.getPoints(parkPoint.getPoints())) <= 20){
                //判断是否在站点附近
                return RidingPositionConstant.NEAR_PARK_POINT;
            }
        }
        //校验禁行区
        List<Forbidden> forbiddenList = cacheService.getForbidden(accountId);
        for (Forbidden forbidden : forbiddenList) {
            //判断是否在禁行区内
            if (GeoUtil.IsPtInPoly(new Point(lon, lat), GeoUtil.getPoints(forbidden.getPoints()))) {
                return RidingPositionConstant.IS_NO_RIDING;
            }
            //判断是否在禁行附近
            if (GeoUtil.getDistance(new Point(lon, lat), GeoUtil.getPoints(forbidden.getPoints())) <= 50){
                return RidingPositionConstant.NEAR_NO_RIDING;
            }
        }
        //校验禁停区
        List<ProhibitArea> area = cacheService.getByArea(accountId);
        for (ProhibitArea prohibitArea : area) {
            //判断是否在禁停区内
            if (GeoUtil.IsPtInPoly(new Point(lon, lat), GeoUtil.getPoints(prohibitArea.getPoints()))) {
                return RidingPositionConstant.IS_NO_PARKING;
            }
            //判断是否在禁停区附近
            if (GeoUtil.getDistance(new Point(lon, lat), GeoUtil.getPoints(prohibitArea.getPoints())) <= 20) {
                return RidingPositionConstant.NEAR_NO_PARKING;
            }
        }
        if (returnModel == 1){
            return RidingPositionConstant.IS_PARK_POINT;
        }
        return RidingPositionConstant.IS_AREA;
    }


    /**
     * 实名认证校验
     */
    public void checkNameAuth(Integer userId, Integer brandId, Integer accountId, String userName, String idNO){
        log.info("[实名认证] 品牌{} 区域{} 用户{} 进行实名认证", brandId, accountId, userId);
        authCount(userId, accountId);
        // 判断区域内是否有运营自行车
        boolean isBikeExist = machineDao.checkIfExistBike(accountId) != null;
        Set<String> targetConfigKeys = new HashSet<>();
        targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MIN_KEY);
        targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MAX_KEY);
        Map<String, String> configMap = accountConfigExtDao.selectByAccountIdAndKeys(accountId, targetConfigKeys).stream()
                .collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
        int minAge = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MIN_KEY))
                .map(Integer::parseInt)
                // 如果有自行车运营，则设置默认最小年龄为 12，否则 16
                .orElse(isBikeExist ? CERTIFICATION_MIN_AGE_BIKE : CERTIFICATION_MIN_AGE_ELECTRIC_BIKE);
        int maxAge = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MAX_KEY))
                .map(Integer::parseInt)
                .orElse(CERTIFICATION_MAX_AGE);

        // 是否满16周岁
        if (!AgeUtil.checkIsMan(idNO, minAge)) {
            throw new BaseException("-3025", "未满 " + minAge + " 周岁");
        }

        if (AgeUtil.checkIsMan(idNO, maxAge)) {
            throw new BaseException("-3057", "用户年龄超出" + maxAge + "周岁，为了您身体安全，限制用车");
        }

        /**身份证号码是否被加入黑名单*/
        List<UserBlackList> userBlackLists = userBlackListService.getByIdNO(idNO);
        if (!userBlackLists.isEmpty()) {
            for (UserBlackList userBlackList : userBlackLists) {
                if (userBlackList != null &&
                        DateTimeUtil.getTimeDF(DateTimeUtil.getNowTime(), userBlackList.getExpireTime() + " 00:00:00") > 0) {
                    throw new BaseException("-3036", "被加入黑名单，功能被限制");
                }
            }
        }

        /*
        校验身份证使用次数
         */
        Map<String, Object> params = new HashMap<>();
        params.put("accountId", brandId);
        params.put("idNO", idNO);
        List<User> userDB = userDao.getByIdNOAndId(params);
        Integer num = 3;
        String authTimes = cacheService.getBrandRemoteConfigValue(brandId, BrandConstant.USER_NAMEAUTH_NUM);
        if (StringUtils.isNotBlank(authTimes)) {
            num = Integer.valueOf(authTimes);
        }
        if (userDB.size() >= num) {
            throw new BaseException("-3052", "您的身份证绑定账号数已达上限，如需继续绑定，请先解绑其他账号。如有疑问，请联系客服。");
        }
    }

    /**
     * 退款条件校验
     *
     * @param recharge
     */
    public boolean checkIsOk(Recharge recharge) {
        switch (recharge.getType()) {
            //退款时限限制，微信12个月
            case 1:
            case 2:
                if (System.currentTimeMillis() >= DateTimeUtil
                        .addMonth(DateTimeUtil.StringToDateTime(recharge.getPayTime()), 12).getTime()) {
                    return false;
                }
                break;
            //支付宝3个月
            case 3:
            case 5:
                if (System.currentTimeMillis() >= DateTimeUtil
                        .addMonth(DateTimeUtil.StringToDateTime(recharge.getPayTime()), 3).getTime()) {
                    return false;
                }
                break;
            //汇付6个月
            case 7:
            case 8:
                if (System.currentTimeMillis() >= DateTimeUtil
                        .addMonth(DateTimeUtil.StringToDateTime(recharge.getPayTime()), 6).getTime()) {
                    return false;
                }
                break;
            default:
                return true;
        }
        return true;
        /**退款时限限制，微信12个月，支付宝3个月*/
        /*if (recharge.getType() == 1 || recharge.getType() == 2) {
            Date maxReturnDate = DateTimeUtil
                    .addMonth(DateTimeUtil.StringToDateTime(recharge.getPayTime()), 12);
            if (System.currentTimeMillis() >= maxReturnDate.getTime()) {
                return false;
            }
        } else if (recharge.getType() == 3 || recharge.getType() == 5) {
            Date maxReturnDate = DateTimeUtil
                    .addMonth(DateTimeUtil.StringToDateTime(recharge.getPayTime()), 3);
            if (System.currentTimeMillis() >= maxReturnDate.getTime()) {
                return false;
            }
        }
        return true;*/
    }

    /**
     * 获得可退款订单
     *
     * @param userId
     * @param recharges
     * @return
     */
    @Override
    public List<Recharge> getCanRefundOrder(Integer userId, List<Recharge> recharges) {
        List<Recharge> list = new ArrayList<>();
        /**去除已退款记录*/
        List<UserRefundLog> userRefundLogs = userRefundLogDao.getByUserId(userId);

        Map<String, Double> refundMap = new HashMap<>();
        for (UserRefundLog userRefundLog : userRefundLogs) {
            String orderNO = userRefundLog.getOrderNO();
            Double refundMoney = refundMap.get(orderNO);
            Double newRefundMoney = refundMoney == null ? userRefundLog.getReturnMoney() : userRefundLog.getReturnMoney() + refundMoney;
            refundMap.put(orderNO, newRefundMoney);
        }
        for (Recharge recharge : recharges) {
            // 检查是否为可退款订单
            if(checkIsOk(recharge)){
                Double hasRefundMoney = refundMap.get(recharge.getOrderNO());
                Double money = recharge.getMoney();
                if (hasRefundMoney == null) {
                    recharge.setCanRefundMoney(money);
                    list.add(recharge);
                } else {
                    Double canRefundMoney = money - hasRefundMoney;
                    if (canRefundMoney > 0) {
                        recharge.setCanRefundMoney(canRefundMoney);
                        list.add(recharge);
                    }
                }
            }
        }
        return list;
    }

    @Override
    public void payEvent(Integer userId) {
        Recharge recharge = rechargeDao.getUserNotPayOrder(userId);
        if (recharge != null) {
            log.info("订单号={}，userId={}，phone={}，收到小程序支付成功事件主动上报", recharge.getOrderNO(), userId, recharge.getPhone());
            if (Integer.valueOf(RechargeConstant.recharge_returnBike).equals(recharge.getRechargeType())) {
                //到这里就默认已支付，避免重复支付充值结束订单
                redisService.add(RedisConstant.RECHARGE_RETURNBIKE_PAYMENT_FINISH + userId, recharge.getOrderNO(), chargeReturnBikeGapSeconds);
            }
        }
    }

    @Override
    public Integer getUserRidingScore(Integer userId) {
        Integer userRidingScore = userDao.getUserRidingScore(userId);
        if (userRidingScore == null) {
            userRidingScore = 260;
        }
        return userRidingScore;
    }

    @Override
    public void updateUserRidingScore(Integer userId, Integer ridingScore) {
        userDao.updateUserRidingScore(userId, ridingScore);
    }

    @Override
    public List<Integer> getUserIdByIdNo(String idNO, Integer accountId) {
        return userDao.getUserIdByIdNo(idNO, accountId);
    }

    @Override
    public List<User> checkLogin(String code, Integer accountId) {
        /**支付配置信息(1:微信APP支付；2：微信小程序支付)*/
        WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAccountId(accountId, 2);
        if (weixinPayConfig == null) {
            throw new BaseException("-2006", "该品牌微信支付未配置");
        }
        String openId = payPointService.getOpenid(code, weixinPayConfig.getAppId(), weixinPayConfig.getSecret());
        if (openId == null) {
            return new ArrayList<>();
        }
        List<User> users = userWxzffService.getUserByOpenId(openId, accountId);
        User shareUser = null;
        if (!users.isEmpty() && !users.get(0).getNameAuth()) {
            // 判断用户是否已经实名，如果未实名，则进行其它品牌实名同步
            User user = users.get(0);
            shareUser = brandShareRelationService.checkNameAuth(user.getUserId(), user.getPhone(),user.getAccountId());
        }

        for (User user : users) {
            // 实名认证用户，同步其它品牌实名信息
            if (Objects.nonNull(shareUser)) {
                user.setIdNO(shareUser.getIdNO());
                user.setName(shareUser.getName());
                user.setNameAuth(true);
            }
            /*放到内存中*/
            String token = tokenService.storeWithToken(user);
            user.setToken(token);
            userOpLogService.insertSingle(UserOpLog.builder()
                    .accountId(accountId)
                    .operation("用户账号")
                    .opType("登录")
                    .opUser(user.getName())
                    .opUserPhone(user.getPhone())
                    .targetUser(user.getName())
                    .targetUserPhone(user.getPhone())
                    .opPlatform(OperationPlatform.CLIENT.code)
                    .opTime(LocalDateTime.now())
                    .result(true)
                    .build());
        }
        return users;
    }


    /**
     * 人脸识别次数统计
     * @param flag 人脸识别结果
     * @param faceCheck faceCheck
     */
    /*public void faceCheckAdd(Boolean flag, FaceCheck faceCheck,User user, Integer accountId) {
        //获取平台人脸配置
        AccountConfigExt ext = cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.FACE_TOTAL_SHORT_TIME);
        faceCheck = Objects.isNull(faceCheck) ? new FaceCheck() : faceCheck;
        //人脸校验失败
        if (!flag) {
            if (Objects.isNull(faceCheck.getShortTimeFailCount())) {
                faceCheck.setShortTimeFailCount(1);
                faceCheck.setShortTime(Objects.isNull(addMinute(Integer.parseInt(ext.getParamValue()), 0)) ? null : addMinute(Integer.parseInt(ext.getParamValue()), 0));
            } else {
                faceCheck.setShortTimeFailCount((int) NumberUtil.add(1, faceCheck.getShortTimeFailCount().intValue()));
            }
        }
        //短时间总次数
        if (Objects.isNull(faceCheck.getShortTimeCount())) {
            faceCheck.setShortTimeCount(1);
            if (Objects.isNull(faceCheck.getShortTime())) {
                faceCheck.setShortTime(Objects.isNull(addMinute(Integer.parseInt(ext.getParamValue()), 0)) ? null : addMinute(Integer.parseInt(ext.getParamValue()), 0));
            }
        } else {
            faceCheck.setShortTimeCount((int) NumberUtil.add(1, faceCheck.getShortTimeCount().intValue()));
        }

        if (Objects.isNull(faceCheck.getTodayCount())) {
            faceCheck.setTodayCount(1);
            faceCheck.setTodayTime(Objects.isNull(addMinute(15, 0)) ? null : addMinute(15, 0));
//            faceCheck.setTodayTime(Objects.isNull(addMinute(24,1)) ? null : addMinute(24,1));
        } else {
            faceCheck.setTodayCount((int) NumberUtil.add(1, faceCheck.getTodayCount().intValue()));
        }

        String faceCheckKey = String.format("%s_%s", RedisConstant.FACE_CHECK, user.getUserId());
        redisService.add(faceCheckKey, JSON.toJSONString(faceCheck), 60 * 60 * 24);

    }*/


    /**
     * 退款
     *
     * @param adaPayConfig
     * @param adaPayLog
     */
    public boolean refund(AdaPayConfig adaPayConfig, AdaPayLog adaPayLog) {
        List<UserAdaPayLog> userAdaPayLogs = userAdaPayLogDao.getByPaymentId(adaPayLog.getAdaPayId());
        List<DivMembers> list = new ArrayList<>();
        for (UserAdaPayLog userAdaPayLog : userAdaPayLogs) {
            DivMembers divMembers = new DivMembers(userAdaPayLog.getUserNO(), String.format("%.2f", userAdaPayLog.getMoney()), "N");
            list.add(divMembers);
        }
        String orderNO = WeixinPayUtil.getReturnNonceStr();
        Map<String, Object> payment = PaymentUtil
                .executeRefund(adaPayConfig, adaPayLog.getAdaPayId(), adaPayLog.getCashMoney(), orderNO, JSON.toJSONString(list));
        Boolean refund = false;
        if (payment.get("status").equals(AdaPayConstant.PAY_STATUS_PENDING) || payment.get("status").equals(
                AdaPayConstant.PAY_STATUS_SUCCEEDED)) {
            refund = true;
        }
        return refund;
    }

    @Override
    public void updateDepositDate(Integer userId, String depositDate) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("userId", userId);
        params.put("depositDate", depositDate);
        params.put("updateTime", DateTimeUtil.getNowTime());


        userDao.updateDepositDate(params);
    }

    /**
     * 华为云人脸识别
     * 注：人脸比对两张图片总大小小于8MB，分辨率小于4096*4096		静态活体检测 图片大小建议小于1MB,分辨率小于4096*2160
     *
     * @param base64Str
     * @param faceData
     * @param huaweiMap
     * @param ossConfig
     * @param user
     * @param accountId 区域id
     * @return
     */

    private Boolean huaweiCloudFaceCompare(String base64Str, UserFaceData faceData, Map<String, String> huaweiMap
            , OssConfig ossConfig, User user, Integer accountId, Map<String, Object> pushMap) {
        pushMap.put("request_result", false);
        pushMap.put("similarity", 0);
        pushMap.put("isTrue", false);
        // 上传图片作为分析
        OssConfig ossConfig1 = new OssConfig();
        BeanUtils.copyProperties(ossConfig, ossConfig1);
        ossConfig1.setBucketName(SystemConstant.FACEBUCKETNAME);
        //下载oss文件转成base64，使用后删除本地文件
        String path = SystemConstant.PHOTO_PATH + "/" + user.getUserId() + ".jpg";
        try {
            // 上传开关
            boolean isUpload = false;
            BrandRemoteConfig brandRemoteConfig = cacheService.getBrandRemoteConfig(user.getAccountId(), BrandConstant.BRAND_LIVE_PHOTO);
            if (brandRemoteConfig != null && Integer.valueOf(brandRemoteConfig.getParamKV().substring(brandRemoteConfig.getParamKV().indexOf("=") + 1)) == 1) {
                isUpload = true;
            }

            Boolean faceResult = false;

            OssFileUtils.upload(ossConfig, faceData.getUrl(), path);
            String oldBase64 = "";
            try {

                oldBase64 = FaceUtil.imageToBase64(path);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            //华为人脸比对接口

            String result = FaceCompareUtil.faceCompare(base64Str, oldBase64, huaweiMap);
            JSONObject jsonObject = JSON.parseObject(result);

            if (jsonObject.getString("error_code") != null) {
                log.error("华为云认证异常" + result);
            }
            Double similarity = jsonObject.getDouble("similarity");

            Double hwy_similarity = getConfigValue(accountId, AccountConfigExtConstant.ACCOUNTCONFIG_FACE_HWY);
            if (similarity != null && similarity * 100 > hwy_similarity) {
                faceResult = true;
            } else {
                log.warn("华为云人脸比对结果失败用户:{};明细:{};对比度:{}", JSON.toJSONString(user), result, similarity);
            }
            log.info("华为云人脸比对结果:{}", faceResult);

            // 上传到oss
            if (isUpload && StringUtils.isNotEmpty(SystemConstant.FACEBUCKETNAME)) {
                // 上报基准照片和新照片
                // 人脸比对（uuid（表示某一次对比，流水号的意思，每次对比的2个照片这个一样）.类型（0:基准照片；1:对比照片）.用户id.匹配度)
                String uid = UUID.randomUUID().toString();
                String imageIdOr = String.format("face/%s.0.%d.%d", uid, user.getUserId(), faceData.getSimilarity().intValue());
                String imageIdNew = String.format("face/%s.1.%d.%d", uid, user.getUserId(), (int) (similarity * 100));
                OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(oldBase64), ossConfig1, imageIdOr, "jpg", OssFileUtils.IMAGE);
                OssFileUtils.uploadByAliYun(OssFileUtils.base64Toio(base64Str), ossConfig1, imageIdNew, "jpg", OssFileUtils.IMAGE);
            }
            pushMap.put("request_result", true);
            pushMap.put("similarity", similarity * 100);
            pushMap.put("isTrue", faceResult);
            return faceResult;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            FileUtils.deleteFile(path);
        }
        return false;
    }


    /**
     * 人脸识别次数限制检查
     *
     * @param userId 用户信息
     */
    public FaceCheck faceTimesCheck(Integer userId) {
        String faceCheckKey = String.format("%s_%s", RedisConstant.FACE_CHECK, userId);
        //获取锁定配置
        String face = redisService.get(faceCheckKey);
        if (Objects.isNull(face)) {
            return new FaceCheck();
        }
        FaceCheck faceCheck = JSON.parseObject(face, FaceCheck.class);
        Date lockTime = faceCheck.getLockTime();
        //如果已锁定
        if (Objects.nonNull(lockTime) && lockTime.after(new Date())) {
            //获取对应锁异常抛出
            throw new BaseException(faceCheck.getLockDesc());
        }
        return faceCheck;
    }

    /**
     * 人脸识别次数统计
     *
     * @param flag      人脸识别结果
     * @param faceCheck faceCheck
     */
    public void faceCheckAdd(boolean flag, FaceCheck faceCheck, User user, Integer accountId) {
        String faceCheckKey = String.format("%s_%s", RedisConstant.FACE_CHECK, user.getUserId());
        //默认配置参数
        int faceCount = 20;
        int shortTime = 10;
        int shortTimeFaceCount = 10;
        int shortTimeFailCount = 6;
        //获取平台人脸配置
        List<AccountConfigExt> accountConfigExtList = cacheService.getAccountConfigExt(accountId);
        for (AccountConfigExt accountConfigExt : accountConfigExtList) {
            if (AccountConfigExtConstant.FACE_TOTAL_EVERYDAY.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                faceCount = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_TOTAL_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTime = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTimeFaceCount = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_FAIL_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTimeFailCount = Integer.parseInt(accountConfigExt.getParamValue());
            }
        }
        //如果配置的都是0 -> 限制
        if (faceCount == 0 || shortTime == 0 || shortTimeFaceCount == 0 || shortTimeFailCount == 0) {
            throw new BaseException("人脸配置参数有误，请检查配置参数");
        }
        Date now = new Date();
        if (faceCheck.getTodayCount() == 0) {
            //如果是第一次进来,今日限制24小时
            faceCheck.setTodayTime(DateUtil.offsetMinute(now, 24 * 60));
        }
        if (faceCheck.getShortTime() != null && faceCheck.getShortTime().before(now)) {
            faceCheck.setShortTimeFailCount(0);
            faceCheck.setShortTimeCount(0);
        }
        if (Objects.nonNull(faceCheck.getLockTime()) && faceCheck.getLockTime().before(now)) {
            faceCheck.setLockType(null);
            faceCheck.setLockTime(null);
        }
        //校验24小时人脸次数
        faceCheck = checkLock(faceCheck.getTodayCount(), faceCount, 24 * 60, FaceTimeCheckEnum.TODAY_TOTAL, faceCheck, now);
        if (!flag) {
            //校验短时失败次数
            faceCheck = checkLock(faceCheck.getShortTimeFailCount(), shortTimeFailCount, shortTime, FaceTimeCheckEnum.SHORT_TERM_FAILURE, faceCheck, now);
        }
        //校验短时人脸次数
        faceCheck = checkLock(faceCheck.getShortTimeCount(), shortTimeFaceCount, shortTime, FaceTimeCheckEnum.SHORT_TERM, faceCheck, now);
        //更新key
        redisService.add(faceCheckKey, JSON.toJSONString(faceCheck), DateUtil.between(new Date(), faceCheck.getTodayTime(), DateUnit.SECOND));
    }

    public FaceCheck checkLock(int currCount, int configCount, int configTime, FaceTimeCheckEnum faceTimeCheckEnum, FaceCheck faceCheck, Date now) {
        currCount++;
        Date expTime = null;
        if (currCount == 1) {
            //隔一段时间的第一次，记录过期时间
            expTime = DateUtil.offsetMinute(now, configTime);
        }
        if (currCount >= configCount) {
            //超出了数量，设置锁定时间
            faceCheck.setLockTime(DateUtil.offsetMinute(now, configTime));
            //设置锁定类型
            faceCheck.setLockType(faceTimeCheckEnum.getLockType());
            faceCheck.setLockDesc(faceTimeCheckEnum.getDesc(configTime));
        }
        switch (faceTimeCheckEnum) {
            case SHORT_TERM_FAILURE:
                faceCheck.setShortTimeFailCount(currCount);
                faceCheck.setShortTime(expTime == null ? faceCheck.getShortTime() : expTime);
                break;
            case SHORT_TERM:
                faceCheck.setShortTimeCount(currCount);
                faceCheck.setShortTime(expTime == null ? faceCheck.getShortTime() : expTime);
                break;
            case TODAY_TOTAL:
                faceCheck.setTodayCount(currCount);
                break;
            default:
                break;
        }
        return faceCheck;

    }


    /**
     * 人脸识别次数限制
     * @param user 用户信息
     */
    /*public FaceCheck faceTimesCheck(User user, Integer accountId){
        String faceCheckKey = String.format("%s_%s", RedisConstant.FACE_CHECK, user.getUserId());
        //先校验是否锁定
        String face = redisService.get(faceCheckKey);
        if (Objects.isNull(face)){
            return null;
        }
        FaceCheck faceCheck = JSON.parseObject(face, FaceCheck.class);
        //判断锁的过期时间是否大于当前时间 是->根据锁类型抛出对应异常
        if (Objects.nonNull(faceCheck.getLockKey()) && Objects.nonNull(faceCheck.getLockTime()) && faceCheck.getLockTime().before(new Date())) {
            switch (faceCheck.getLockKey()) {
                case 0:
                    throw new BaseException("短时间内人脸校验失败次数过多，为了确保您的账户安全，请10分钟后再尝试。如有疑问，请联系客服了解详情");
                case 1:
                    throw new BaseException("短时间内人脸校验过于频繁，为了确保您的账户安全，请10分钟后再尝试。如有疑问，请联系客服了解详情");
                case 2:
                    throw new BaseException("人脸校验已超过今日上限，为了确保您的账户安全，请24小时后再尝试。如有疑问，请联系客服了解详情");
                default:
                    break;
            }
        }

        //配置参数
        int faceCount = 20;
        int shortTime = 20;
        int shortTimeFaceCount = 10;
        int shortTimeFailCount = 6;
        //获取平台人脸配置
        List<AccountConfigExt> accountConfigExtList = cacheService.getAccountConfigExt(accountId);
        for (AccountConfigExt accountConfigExt : accountConfigExtList) {
            if (AccountConfigExtConstant.FACE_TOTAL_EVERYDAY.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                faceCount = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_TOTAL_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTime = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTimeFaceCount = Integer.parseInt(accountConfigExt.getParamValue());
            } else if (AccountConfigExtConstant.FACE_FAIL_SHORT_TIME.equalsIgnoreCase(accountConfigExt.getParamKey())) {
                shortTimeFailCount = Integer.parseInt(accountConfigExt.getParamValue());
            }
        }
        //如果配置的都是0 -> 限制
        if (faceCount == 0 || shortTime == 0 || shortTimeFaceCount == 0 || shortTimeFailCount == 0) {
            throw new BaseException("人脸配置参数有误，请检查配置参数");
        }

        //判断短时间失败次数
        if (Objects.nonNull(faceCheck.getShortTimeFailCount()) && Objects.nonNull(faceCheck.getShortTime())) {
            if (faceCheck.getShortTimeFailCount() >= shortTimeFailCount) {
                faceCheck.setLockKey(0);
                faceCheck.setLockTime(Objects.isNull(addMinute(10, 0)) ? null : addMinute(10, 0));
                log.info("{}_{}_短时间内人脸校验失败次数过多,短时间内失败次数:{}", user.getUserId(), user.getName(), faceCheck.getShortTimeFailCount());
                throw new BaseException("短时间内人脸校验失败次数过多，为了确保您的账户安全，请10分钟后再尝试。如有疑问，请联系客服了解详情");
            }
        }
        //短时间内使用次数
        if (Objects.nonNull(faceCheck.getShortTimeCount()) && Objects.nonNull(faceCheck.getShortTime())) {
            if (faceCheck.getShortTimeCount() >= shortTimeFaceCount) {
                faceCheck.setLockKey(1);
                faceCheck.setLockTime(Objects.isNull(addMinute(10, 0)) ? null : addMinute(10, 0));
                log.info("{}_{}_短时间内人脸校验过于频繁,短时间内人脸使用次数:{}", user.getUserId(), user.getName(), faceCheck.getShortTimeCount());
                throw new BaseException("短时间内人脸校验过于频繁，为了确保您的账户安全，请10分钟后再尝试。如有疑问，请联系客服了解详情");
            }
        }

        //今日使用次数
        if (Objects.nonNull(faceCheck.getTodayCount()) && Objects.nonNull(faceCheck.getTodayTime())) {
            if (faceCheck.getTodayCount() >= faceCount) {
                faceCheck.setLockKey(2);
                //todo 配合测试改为15分钟
                faceCheck.setLockTime(Objects.isNull(addMinute(15, 0)) ? null : addMinute(15, 0));
//                faceCheck.setLockTime(Objects.isNull(addMinute(24,1)) ? null: addMinute(24, 1));
                log.info("{}_{}_今日人脸校验过于频繁,今日人脸使用次数:{}", user.getUserId(), user.getName(), faceCheck.getTodayCount());
                throw new BaseException("人脸校验已超过今日上限，为了确保您的账户安全，请24小时后再尝试。如有疑问，请联系客服了解详情");
            }
        }
        //更新缓存
        redisService.add(faceCheckKey, JSON.toJSONString(faceCheck), redisService.getExpiration(faceCheckKey));
        return faceCheck;
    }*/

    /**
     * 当前时间增加#{time}
     *
     * @param time 时间
     * @param type 类型 0：分钟 1：小时
     * @return
     */
    private Date addMinute(Integer time, Integer type) {
        try {
            Date date = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            switch (type) {
                case 0:
                    calendar.add(Calendar.MINUTE, time);
                    break;
                case 1:
                    calendar.add(Calendar.HOUR, time);
            }
            return calendar.getTime();
        } catch (Exception e) {
            log.error("增加时间异常:{}", e);
        }
        return null;
    }

    /**
     * 用户欠款校验
     *
     * @param userId
     */
    private void validRefund(Integer userId) {
        User user = userDao.getByUserId(userId);
        if (user.getRideMoney() > 0) {
            throw new BaseException("当前存在欠款，请清除欠款后再操作");
        }
    }

    /**
     * 获取用户注销次数
     * @param accountId
     * @param phone
     * @return
     */
    @Override
    public Integer getCountByAccountIdAndPhone(Integer accountId, String phone) {
        return userDao.getCountByAccountIdAndPhone(accountId, phone, false);
    }

    /**
     * 获取用户注销次数
     * @param accountId
     * @param phone
     * @return
     */
    @Override
    public Integer getCountByAccountIdAndPhoneNearlyYear(Integer accountId, String phone) {
        return userDao.getCountByAccountIdAndPhone(accountId, phone, true);
    }

    /**
     * 更新所有非空字段
     * @param user
     */
    @Override
    public void updateAll(User user) {
        userDao.updateAll(user);
    }

    @Override
    public User getByShareBrandAndHasNameAuth(List<Integer> brandIds, String phone) {
        return userDao.getByShareBrandAndHasNameAuth(brandIds, phone);
    }

    @Override
    public List<Integer> getBrandIdsByShareConfig(Integer brandId) {
        return userDao.getBrandIdsByShareConfig(brandId);
    }

    @Override
    public void updateUserMoney(Integer userId, Long money, Date time) {
        userDao.updateUserMoney(userId, money, time);
    }
}